青铜版本
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
}
}