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
相关产品推荐
相关产品推荐

