You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android 8.1 使用CreateDocument保存重名文件时文件名格式异常问题

问题根因

该异常是Android 12(API Level 32)及以上系统内置DocumentsUI组件的实现缺陷导致的:系统自动处理重名文件的逻辑被错误修改,将序号直接拼接在完整文件名末尾,而非插入文件名主体和后缀之间,Android 11及更低版本不存在该问题。

解决方案
  • 方案1:预先查询重名文件,主动生成合规文件名传入,规避系统自动重命名逻辑
    实现逻辑为:先通过ContentResolver查询目标保存路径下的已存在文件列表,匹配和目标文件名前缀、后缀一致的文件,统计序号后生成file(1).csv格式的合规文件名,再传入launch方法启动创建,从根源避免触发系统的自动重命名逻辑。
    示例代码:
// 生成带正确序号的目标文件名
fun generateValidFileName(context: Context, baseName: String, suffix: String): String {
    var count = 1
    var finalName = "$baseName$suffix"
    val projection = arrayOf(MediaStore.MediaColumns.DISPLAY_NAME)
    // 查询公共下载目录下的文件,可根据实际保存路径调整uri
    val queryUri = MediaStore.Downloads.EXTERNAL_CONTENT_URI
    context.contentResolver.query(queryUri, projection, null, null, null)?.use { cursor ->
        val nameColumn = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DISPLAY_NAME)
        val existNames = mutableListOf<String>()
        while (cursor.moveToNext()) {
            existNames.add(cursor.getString(nameColumn))
        }
        while (existNames.contains(finalName)) {
            finalName = "$baseName($count)$suffix"
            count++
        }
    }
    return finalName
}

// 调用时使用生成的文件名
val targetFileName = generateValidFileName(requireContext(), "file", ".csv")
getContent.launch(targetFileName)
  • 方案2:创建完成后手动修正错误命名
    如果无法预先查询路径文件,可在拿到系统返回的uri后校验文件名格式,若符合*.后缀(序号)的错误格式,主动调用系统接口重命名为正确格式:
// 注册时建议明确指定MIME类型,避免系统识别文件类型错误
private val getContent = registerForActivityResult(ActivityResultContracts.CreateDocument("text/csv")) { uri ->
    uri?.let {
        // 先修正文件名再写入内容
        fixFileNameIfNeeded(requireContext(), it)
        writeToFile(requireActivity(), it)
    }
}

fun fixFileNameIfNeeded(context: Context, uri: Uri) {
    val projection = arrayOf(OpenableColumns.DISPLAY_NAME)
    context.contentResolver.query(uri, projection, null, null, null)?.use { cursor ->
        if (cursor.moveToFirst()) {
            val name = cursor.getString(cursor.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME))
            // 匹配类似file.csv(1)的错误格式
            val regex = Regex("(.+)(\\.\\w+)\\((\\d+)\\)$")
            val matchResult = regex.find(name)
            if (matchResult != null) {
                val (baseName, suffix, num) = matchResult.destructured
                val correctName = "$baseName($num)$suffix"
                // 刚创建的文件默认持有写入权限,可直接调用重命名接口
                DocumentsContract.renameDocument(context.contentResolver, uri, correctName)
            }
        }
    }
}
  • 方案3:改用MediaStore API写入文件
    如果你的业务不需要用户自主选择保存路径,仅需要导出到公共下载/文档目录,可直接使用MediaStore API写入文件,完全自主控制命名逻辑,不需要依赖系统DocumentsUI组件,从根本上规避该问题。

内容的提问来源于stack exchange,提问作者SoulReaver313

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 01:24:06