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

Android开发:如何修改写文件函数实现数据追加写入功能?

解决方案

你的代码每次调用都会生成新文件的核心原因是:每次执行都会调用contentResolver.insert()向MediaStore插入新的文件记录,哪怕同路径下存在同名文件,系统也会自动生成带序号后缀的新文件避免重名。要实现追加写入,需要先查询目标文件是否存在,存在则直接打开已有文件写入,不存在再走新建逻辑。

调整步骤

  • 新增MediaStore查询逻辑,校验指定路径下的同名文件是否存在
  • 文件存在时直接获取已有Uri,跳过新建流程
  • 两种场景下都保持openOutputStream的wa追加模式即可

完整修改后代码

fun saveDocument(context : Context, fileName: String, text : String): Boolean {
    try {
        val collection = MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
        val dirDest = File(Environment.DIRECTORY_DOCUMENTS, context.getString(R.string.app_name))
        val relativePath = "$dirDest${File.separator}"

        // 先查询已有文件
        val existingUri = getExistingFileUri(context, fileName, relativePath)
        val targetUri = if (existingUri != null) {
            // 文件已存在,直接复用Uri,先设置为pending状态避免写入中被其他应用访问
            val contentValues = ContentValues()
            contentValues.put(MediaStore.Files.FileColumns.IS_PENDING, 1)
            context.contentResolver.update(existingUri, contentValues, null, null)
            existingUri
        } else {
            // 文件不存在,走新建逻辑
            val contentValues = ContentValues().apply {
                put(MediaStore.MediaColumns.DISPLAY_NAME, fileName)
                put(MediaStore.MediaColumns.RELATIVE_PATH, relativePath)
                put(MediaStore.Files.FileColumns.IS_PENDING, 1)
            }
            context.contentResolver.insert(collection, contentValues)
        }

        targetUri?.let { uri ->
            // 追加模式写入内容
            context.contentResolver.openOutputStream(uri, "wa").use { out -> 
                out?.write(text.toByteArray()) 
            }
            // 写入完成,取消pending状态
            val contentValues = ContentValues()
            contentValues.put(MediaStore.Files.FileColumns.IS_PENDING, 0)
            context.contentResolver.update(uri, contentValues, null, null)
            return true
        }
        return false
    } catch (e: Exception) {
        e.printStackTrace()
        return false
    }
}

/**
 * 查询指定路径下的同名文件是否存在,存在则返回对应的Uri
 */
private fun getExistingFileUri(context: Context, fileName: String, relativePath: String): Uri? {
    val projection = arrayOf(MediaStore.Files.FileColumns._ID)
    // 筛选条件:文件名匹配、路径匹配、文件不是pending状态
    val selection = "${MediaStore.MediaColumns.DISPLAY_NAME} = ? " +
            "AND ${MediaStore.MediaColumns.RELATIVE_PATH} = ? " +
            "AND ${MediaStore.Files.FileColumns.IS_PENDING} = 0"
    val selectionArgs = arrayOf(fileName, relativePath)
    
    context.contentResolver.query(
        MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY),
        projection,
        selection,
        selectionArgs,
        null
    )?.use { cursor ->
        if (cursor.moveToFirst()) {
            val fileId = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns._ID))
            return ContentUris.withAppendedId(MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY), fileId)
        }
    }
    return null
}

注意事项

  • Android 13及以上版本不需要申请额外存储权限即可读写公共目录下的自有文件,Android 10-12需要申请READ_EXTERNAL_STORAGE、WRITE_EXTERNAL_STORAGE权限,更低版本需要申请存储运行时权限。
  • 如果需要兼容Android 10以下版本,不能直接使用RELATIVE_PATH参数,需要单独做版本适配。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 21:36:02