青铜版本
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? = nullif ("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.columnNamesLogUtils.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 nullLogUtils.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: Intwhile (inputStream.read(buffer).also { bytesRead = it } != -1) {outputStream.write(buffer, 0, bytesRead)}// 关闭流inputStream.close()outputStream.close()destFile} catch (e: Exception) {e.printStackTrace()null}
}