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

安卓Kotlin应用如何实现公共Documents文件夹的文件读写?

安卓Kotlin写入公共Documents目录实现方案

你当前使用的Context.openFileOutput和Context.openFileInput读写的是应用私有内部存储,路径为/data/data/[你的应用包名]/files/,该目录下的文件仅你的应用可访问,不会在系统公共目录中展示,所以无法在Documents文件夹中找到对应文件。

要实现将文件保存到系统公共Documents目录,可按以下步骤修改:

1. 清单文件权限配置

在AndroidManifest.xml中添加对应权限,做版本兼容:

<!-- 安卓9(API 28)及以下需要读写存储权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="28" />

<!-- 如果targetSdkVersion=29,可在application节点添加以下属性做兼容适配 -->
<application
    ...
    android:requestLegacyExternalStorage="true">
    ...
</application>

注意:安卓6.0(API 23)到安卓9版本,需要在代码中动态申请读写存储权限,仅静态声明不生效。

2. 写入公共Documents目录方法

替换你原有的save方法,适配不同安卓版本:

fun saveTextToPublicDocuments(nomeFile: String, testo: String, contesto: Context) {
    // 安卓10(API 29)及以上使用MediaStore写入公共目录,无需申请存储权限
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        // 先查询是否已存在同名文件,存在就追加内容,不存在就新建
        var targetUri: Uri? = null
        val projection = arrayOf(MediaStore.MediaColumns._ID)
        val selection = "${MediaStore.MediaColumns.DISPLAY_NAME} = ? AND ${MediaStore.MediaColumns.RELATIVE_PATH} = ?"
        val selectionArgs = arrayOf(nomeFile, "${Environment.DIRECTORY_DOCUMENTS}/")
        
        contesto.contentResolver.query(
            MediaStore.Files.getContentUri("external"),
            projection,
            selection,
            selectionArgs,
            null
        )?.use { cursor ->
            if (cursor.moveToFirst()) {
                val id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.MediaColumns._ID))
                targetUri = ContentUris.withAppendedId(MediaStore.Files.getContentUri("external"), id)
            }
        }

        if (targetUri == null) {
            // 无同名文件,新建文件
            val contentValues = ContentValues().apply {
                put(MediaStore.MediaColumns.DISPLAY_NAME, nomeFile)
                put(MediaStore.MediaColumns.MIME_TYPE, "text/plain")
                put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOCUMENTS)
            }
            targetUri = contesto.contentResolver.insert(MediaStore.Files.getContentUri("external"), contentValues)
        }

        targetUri?.let { uri ->
            // 加"wa"模式实现追加写入,对应你原有的MODE_APPEND逻辑
            contesto.contentResolver.openOutputStream(uri, "wa")?.use { outputStream ->
                outputStream.write(testo.toByteArray())
            }
        }
    } else {
        // 安卓9及以下直接写入公共Documents路径
        val docsDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS)
        val targetFile = File(docsDir, nomeFile)
        // 第二个参数true代表追加模式
        FileOutputStream(targetFile, true).use { outputStream -> 
            outputStream.write(testo.toByteArray())
        }
    }
}

3. 读取公共Documents目录文件方法

替换原有read方法:

fun readTextFromPublicDocuments(nomeFile: String, contesto: Context): String? {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        val projection = arrayOf(MediaStore.MediaColumns._ID)
        val selection = "${MediaStore.MediaColumns.DISPLAY_NAME} = ? AND ${MediaStore.MediaColumns.RELATIVE_PATH} = ?"
        val selectionArgs = arrayOf(nomeFile, "${Environment.DIRECTORY_DOCUMENTS}/")
        
        contesto.contentResolver.query(
            MediaStore.Files.getContentUri("external"),
            projection,
            selection,
            selectionArgs,
            null
        )?.use { cursor ->
            if (cursor.moveToFirst()) {
                val id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.MediaColumns._ID))
                val uri = ContentUris.withAppendedId(MediaStore.Files.getContentUri("external"), id)
                contesto.contentResolver.openInputStream(uri)?.use { inputStream ->
                    return inputStream.bufferedReader().use { it.readText() }
                }
            }
        }
    } else {
        val docsDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS)
        val targetFile = File(docsDir, nomeFile)
        if (targetFile.exists()) {
            return FileInputStream(targetFile).bufferedReader().use { it.readText() }
        }
    }
    return null
}

注意事项

  • 上述方法保存的文件会直接出现在系统公共Documents目录下,所有应用都可以访问
  • 如果需要更灵活的文件选择交互,可使用系统存储访问框架(SAF)调用系统文件选择器,让用户主动指定保存/读取路径
  • 若你需要覆盖写入而非追加,把写入时的追加模式参数改为对应覆盖参数即可

内容的提问来源于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 20:24:00