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

Android Studio中如何跨设备获取存储文件的文件名?

跨Android设备获取文件选择器选中文件的文件名问题

我在Android Studio开发Android应用,需要实现读取含JSON的txt文件并重建指定对象(导出报告)的功能,支持用户通过文件选择器选多个文件。

最初用Activity Result Contract获取文件Uri,尝试用Uri.getPath()获取路径无效:一加Android 14设备返回的Uri带文件名,指定固定文件夹后能拼接路径;但三星Galaxy S9 FE(同样Android 14)返回的Uri是一串编号,导致找不到文件,报错:

java.io.FileNotFoundException: /storage/emulated/0/Documents/ToeApp Exported Reports/document%3A1000000041: open failed: ENOENT (No such file or directory)

相关代码:

获取对象的函数

fun getExpRep(uri: Uri): ExportedRep?{
    val path = uri.path?.split("/")
    Log.i("Path last seg" , "${path?.last()}")
    Log.i("Path uri" , "${path}")

    Log.i("Path check", exportsFold.path)
    if (path.isNotNull()){
        if (path!!.isNotEmpty()){
            val filePath = File(exportsFold , path?.last())
            Log.i("path check" , "Filepath: $filePath")
            val content = ByteArray(filePath.length().toInt())
            val stream = FileInputStream(filePath)
            stream.read(content)
            val repJSON = content.decodeToString()
            val expRep = Gson().fromJson(repJSON, ExportedRep::class.java)
            return expRep
        }
        else return  null

    }
    else return null
}

文件夹创建代码

val docFolder = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS)
val exportsFold = File(docFolder, "ToeApp Exported Reports")

fun makeExportsFolder(){
    if (!exportsFold.exists()){
        exportsFold.mkdirs()
    }
    if (!toeAppDir.exists()){
        val makecheck = toeAppDir.mkdirs()
        Log.i("create File", "MakeCheck: $makecheck")
    }                   
}

请问有没有健壮的方法可跨Android设备(至少适配三星设备)获取选中文件的文件名?


解决方案:通过ContentResolver获取文件名

不要依赖Uri.getPath()方法,不同厂商文件选择器返回的Uri格式差异极大,正确做法是通过ContentResolver查询文件元数据获取文件名,同时直接通过Uri打开输入流读取内容,无需拼接路径。

1. 获取文件名的工具函数

fun getFileName(context: Context, uri: Uri): String? {
    val cursor = context.contentResolver.query(uri, null, null, null, null)
    cursor?.use {
        val nameIndex = it.getColumnIndex(android.provider.OpenableColumns.DISPLAY_NAME)
        it.moveToFirst()
        return it.getString(nameIndex)
    }
    return null
}

2. 修改读取文件的逻辑

直接通过ContentResolver打开输入流,跳过构造File对象的步骤:

fun getExpRep(context: Context, uri: Uri): ExportedRep? {
    return try {
        context.contentResolver.openInputStream(uri)?.use { inputStream ->
            val repJSON = inputStream.bufferedReader().readText()
            Gson().fromJson(repJSON, ExportedRep::class.java)
        }
    } catch (e: IOException) {
        e.printStackTrace()
        null
    }
}

说明:

  • OpenableColumns.DISPLAY_NAME是系统标准字段,所有合规的文件选择器都会返回该字段,跨设备兼容性强。
  • 用contentResolver.openInputStream(uri)读取文件是Android推荐的共享文件访问方式,无需关心文件物理路径,彻底规避厂商Uri格式差异问题。
  • 代码中使用use函数自动关闭流,避免资源泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 20:05:14