通过uri获取文件路径手机适配

发布于:2025-04-14 ⋅ 阅读:(22) ⋅ 点赞:(0)

青铜版本

  return contentResolver.query(this, arrayOf(MediaStore.MediaColumns.DATA), null, null).let {
        if (it?.moveToFirst() == true) {
            val columnIndex = it.getColumnIndex(MediaStore.MediaColumns.DATA)
            val path = it.getString(columnIndex)
            it.close()
            return path
        }
        ""
    }

在firebase上发现很多异常奔溃日志,部分手机获取到的 path 是空的,也就是说没有_data字段

其实有的手机通过选择图片或者文件后返回的uri并不一定是媒体uri,也可能是document uri,造成这个时候直接通过 uri查询,找不到_data字段,需要将 document uri中分离出类型和id,在拼凑成新的uri,在通过contentprovider查询,才可以查出 _data字段中真正的路径

//如果是Document类型的URI,需要进行转换
private fun getPathFromDocumentUri(uri: Uri): String? {
    val isDocumentUri = DocumentsContract.isDocumentUri(BaseApplication.instance, uri)
    if (isDocumentUri) {
        val docId = DocumentsContract.getDocumentId(uri)
        val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
        val type = split[0] // "image"
        val id = split[1] // "1044024"
        var quaryUri: Uri? = null
        if ("image".equals(type)) {
            quaryUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
        } else if ("video".equals(type)) {
            quaryUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
        } else if ("audio".equals(type)) {
            quaryUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
        }

        LogUtils.d("linlian quaryUri=$quaryUri")
        quaryUri?.let {
            val projection = arrayOf(MediaStore.Images.Media.DATA,MediaStore.Images.Media._ID)
            BaseApplication.instance.contentResolver.query(
                quaryUri,
                projection,
                MediaStore.Images.Media._ID + "=?",
                arrayOf<String>(id),
                null
            )?.use {cursor ->

                val columnNames = cursor.columnNames
                LogUtils.d("linlian ", "URI: $uri")
                LogUtils.d("linlian ", "Total columns: ${columnNames.size}")

                // 遍历每一行
                while (cursor.moveToNext()) {
//                    val rowData = StringBuilder()
                    // 遍历每一列
//                    for (columnName in columnNames) {
//                        val columnIndex = cursor.getColumnIndex(columnName)
//                        if (columnIndex == -1) {
//                            rowData.append("$columnName: [COLUMN_NOT_FOUND]\n")
//                            continue
//                        }
//
//                        val value = when (cursor.getType(columnIndex)) {
//                            Cursor.FIELD_TYPE_NULL -> "NULL"
//                            Cursor.FIELD_TYPE_INTEGER -> cursor.getLong(columnIndex)
//                            Cursor.FIELD_TYPE_FLOAT -> cursor.getDouble(columnIndex)
//                            Cursor.FIELD_TYPE_STRING -> cursor.getString(columnIndex)
//                            Cursor.FIELD_TYPE_BLOB -> "BLOB (${cursor.getBlob(columnIndex)?.size ?: 0} bytes)"
//                            else -> "UNKNOWN_TYPE"
//                        }
//                        rowData.append("$columnName: $value\n")
//                    }
//                    LogUtils.d("linlian", "Row ${cursor.position}:\n$rowData")

                    val index= cursor.getColumnIndex(MediaStore.Images.Media.DATA)
                    if(index!=-1){
                        val path = cursor.getString(index)
                        LogUtils.d("linlian", "!!!!!!!!!!$path")
                        return path
                    }
                    LogUtils.d("linlian", "!!!!!!!!!!$index")
                }
                return null
            }

        }

    }
    return null
}

但是根据文档其实Android 10 之后是有新的字段,但是手机厂商众多,实现方式不一,还是找不到path怎么办,官方是说可以从 RELATIVE_PATH获取路径

contentResolver.query(uri, projection, null, null, null).use { cursor ->
                if (cursor != null && cursor.moveToFirst()) {
                    // 获取文件名
                    val nameIndex: Int = cursor.getColumnIndex(
                        if (isImage) MediaStore.Images.Media.DISPLAY_NAME else MediaStore.Video.Media.DISPLAY_NAME
                    )
                    val displayName: String? =
                        if ((nameIndex != -1)) cursor.getString(nameIndex) else null

                    // 获取相对路径(可能为 null)
                    val pathIndex: Int = cursor.getColumnIndex(
                        if (isImage) MediaStore.Images.Media.RELATIVE_PATH else MediaStore.Video.Media.RELATIVE_PATH
                    )
                    val relativePath: String? =
                        if ((pathIndex != -1)) cursor.getString(pathIndex) else null
                    LogUtils.d("linlian getPathFromRelativeColumn displayName=$displayName,relativePath=$relativePath")
                    // 生成最终路径
                    return buildPathForAndroidQ(displayName, relativePath)
                }
            }

如果以上都找不到path 怎么办呢

那最后的方式是,通过uri拷贝一份文件到应用目录,不过用完记得删除

fun copyFileFromUri(context: Context, uri: Uri, destFileName: String): File? {
    return try {
        // 打开输入流
        val inputStream: InputStream? = context.contentResolver.openInputStream(uri)
        if (inputStream == null) {
            return null
        }

        // 目标文件:保存在 app 的 filesDir 目录
        val destFile = File(context.filesDir, destFileName)
        val outputStream: OutputStream = FileOutputStream(destFile)

        // 拷贝数据
        val buffer = ByteArray(4096)
        var bytesRead: Int
        while (inputStream.read(buffer).also { bytesRead = it } != -1) {
            outputStream.write(buffer, 0, bytesRead)
        }

        // 关闭流
        inputStream.close()
        outputStream.close()

        destFile
    } catch (e: Exception) {
        e.printStackTrace()
        null
    }
}






网站公告

今日签到

点亮在社区的每一天
去签到