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