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

