android10以后,只需要考虑沙盒里的文件uri和共享文件(匿名uri)的转换,其他类型的,要么转换不成File,要么拿不到,也就不用再考虑了
沙盒里的文件,可以直接转成File使用,共享文件如果要操作,需要先复制到沙盒目录下
kotlin写法
@RequiresApi(Build.VERSION_CODES.Q)
private fun uriToFileQ(context: Context, uri: Uri): File? =
if (uri.scheme == ContentResolver.SCHEME_FILE)
uri.toFile()
else if (uri.scheme == ContentResolver.SCHEME_CONTENT) {
//把文件保存到沙盒
val contentResolver = context.contentResolver
val cursor = contentResolver.query(uri, null, null, null, null)
cursor?.let {
if (it.moveToFirst()) {
val ois = context.contentResolver.openInputStream(uri)
val displayName =
it.getString(it.getColumnIndex(OpenableColumns.DISPLAY_NAME))
ois?.let {
File(
context.externalCacheDir!!.absolutePath,
"${Random.nextInt(0, 9999)}$displayName"
).apply {
val fos = FileOutputStream(this)
android.os.FileUtils.copy(ois, fos)
fos.close()
it.close()
}
}
} else null
}
} else null
java写法
@RequiresApi(api = Build.VERSION_CODES.Q)
public static File uriToFileApiQ(Uri uri) {
File file = null;
//android10以上转换
if (uri.getScheme().equals(ContentResolver.SCHEME_FILE)) {
file = new File(uri.getPath());
} else if (uri.getScheme().equals(ContentResolver.SCHEME_CONTENT)) {
//把文件复制到沙盒目录
ContentResolver contentResolver = context.getContentResolver();
Cursor cursor = contentResolver.query(uri, null, null, null, null);
if (cursor.moveToFirst()) {
String displayName = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
try {
InputStream is = contentResolver.openInputStream(uri);
File cache = new File(context.getExternalCacheDir().getAbsolutePath(), Math.round((Math.random() + 1) * 1000) + displayName);
FileOutputStream fos = new FileOutputStream(cache);
FileUtils.copy(is, fos);
file = cache;
fos.close();
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return file;
}
兼容全版本的写法
fun uriToFile(context: Context, uri: Uri) =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
uriToFileQ(context, uri)
} else uriToFileN(context, uri)
@RequiresApi(Build.VERSION_CODES.Q)
private fun uriToFileQ(context: Context, uri: Uri): File? =
if (uri.scheme == ContentResolver.SCHEME_FILE)
uri.toFile()
else if (uri.scheme == ContentResolver.SCHEME_CONTENT) {
//把文件保存到沙盒
val contentResolver = context.contentResolver
val cursor = contentResolver.query(uri, null, null, null, null)
cursor?.let {
if (it.moveToFirst()) {
val ois = context.contentResolver.openInputStream(uri)
val displayName =
it.getString(it.getColumnIndex(OpenableColumns.DISPLAY_NAME))
ois?.let {
File(
context.externalCacheDir!!.absolutePath,
"${Random.nextInt(0, 9999)}$displayName"
).apply {
val fos = FileOutputStream(this)
android.os.FileUtils.copy(ois, fos)
fos.close()
it.close()
}
}
} else null
}
} else null
private fun uriToFileN(context: Context, uri: Uri): File? {
val authority = uri.authority
val scheme = uri.scheme
val path = uri.path
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && path != null) {
val externals = arrayOf("/external", "/external_path")
externals.forEach {
if (path.startsWith(it + "/")) {
val file = File(
Environment.getExternalStorageDirectory().absolutePath + path.replace(it, "")
)
if (file.exists()) {
return file
}
}
}
}
if (scheme == ContentResolver.SCHEME_FILE)
return uri.toFile()
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && DocumentsContract.isDocumentUri(
context,
uri
)
) {
return if ("com.android.externalstorage.documents" == authority) {
val docId = DocumentsContract.getDocumentId(uri)
val split = docId.split(":").toTypedArray()
val type = split[0]
if ("primary".equals(type, ignoreCase = true)) {
return File(
Environment.getExternalStorageDirectory()
.toString() + "/" + split[1]
)
} else {
val mStorageManager =
context.getSystemService(Context.STORAGE_SERVICE) as StorageManager
val storageVolumeClazz = Class.forName("android.os.storage.StorageVolume")
val getVolumeList = mStorageManager.javaClass.getMethod("getVolumeList")
val getUuid = storageVolumeClazz.getMethod("getUuid")
val getState = storageVolumeClazz.getMethod("getState")
val getPath = storageVolumeClazz.getMethod("getPath")
val isPrimary = storageVolumeClazz.getMethod("isPrimary")
val isEmulated = storageVolumeClazz.getMethod("isEmulated")
val result = getVolumeList.invoke(mStorageManager)
val length = Array.getLength(result)
for (i in 0 until length) {
val storageVolumeElement = Array.get(result, i)
val mounted = Environment.MEDIA_MOUNTED == getState.invoke(storageVolumeElement)
|| Environment.MEDIA_MOUNTED_READ_ONLY == getState.invoke(
storageVolumeElement
)
if (!mounted) continue
if (isPrimary.invoke(storageVolumeElement) as Boolean
&& isEmulated.invoke(storageVolumeElement) as Boolean
) continue
val uuid = getUuid.invoke(storageVolumeElement) as String
if (uuid == type) {
return File(
getPath.invoke(storageVolumeElement).toString() + "/" + split[1]
)
}
}
}
null
} else if ("com.android.providers.downloads.documents" == authority) {
val id = DocumentsContract.getDocumentId(uri)
if (!TextUtils.isEmpty(id)) {
val contentUri = ContentUris.withAppendedId(
Uri.parse("content://downloads/public_downloads"),
java.lang.Long.valueOf(id)
)
return getFileFromUri(context, contentUri)
}
null
}
else if ("com.android.providers.media.documents" == authority) {
val docId = DocumentsContract.getDocumentId(uri)
val split = docId.split(":").toTypedArray()
val type = split[0]
val contentUri: Uri
contentUri = if ("image" == type) {
MediaStore.Images.Media.EXTERNAL_CONTENT_URI
} else if ("video" == type) {
MediaStore.Video.Media.EXTERNAL_CONTENT_URI
} else if ("audio" == type) {
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
} else return null
val selection = "_id=?"
val selectionArgs = arrayOf(split[1])
getFileFromUri(context, contentUri, selection, selectionArgs)
}
else if (ContentResolver.SCHEME_CONTENT == scheme)
getFileFromUri(context, uri)
else
null
}
return null
}
private fun getFileFromUri(
context: Context, uri: Uri, selection: String? = null,
selectionArgs: kotlin.Array? = null
): File? =
context.contentResolver.query(uri, arrayOf("_data"), selection, selectionArgs, null)?.let {
if (it.moveToFirst()) {
it.getColumnIndex(MediaStore.Images.Media.DATA).let {index->
if(index == -1 ) null else File(it.getString(index))
}
} else null
}