一、构建选择文件与回调方法
java
//文件选择回调
ActivityResultLauncher<String[]> pickFile = registerForActivityResult(new ActivityResultContracts.OpenDocument(), uri->{
if (uri != null) {
Log.e("cxy", "返回的uri:" + uri);
Log.e("cxy","Path是:"+ FileUtil.getFilePathByUri(context,uri));
}
});
//启动选择
pickFile.launch(new String[]{"*/*"})
java
//photopicker选择器回调(多选同理)
ActivityResultLauncher<PickVisualMediaRequest> pickMedia =
registerForActivityResult(new ActivityResultContracts.PickVisualMedia(), uri -> {
if (uri != null) {
Log.e("cxy", "返回的uri:" + uri);
Log.e("cxy","Path是:"+ FileUtil.getFilePathByUri(context,uri));
} else {
}
});
//photopicker选择器启动选择,选择模式:ImageOnly、VideoOnly、ImageAndVideo
pickMedia.launch(new PickVisualMediaRequest.Builder()
.setMediaType(isImg ? ActivityResultContracts.PickVisualMedia.ImageOnly.INSTANCE : ActivityResultContracts.PickVisualMedia.VideoOnly.INSTANCE)
.build()));
二、uri转真实路径工具类
备注:
1、针对Download目录先判空,为空则复制文件至内部路径再返回
2、SAF选择文件时,存储设备选择返回的uri路径为:
content://com.android.externalstorage.documents/document/primary%3ADocuments%2Fenvironment.pub
通过选择侧边栏中的图片、视频、音频、文档等分类进入选择文件目录时,返回的路径为:
content://com.android.providers.media.documents/document/image%3A1000010017
content://com.android.providers.media.documents/document/video%3A1000008421
content://com.android.providers.media.documents/document/audio%3A1000000212
content://com.android.providers.media.documents/document/document%3A1000009571
这时如果没有授予存储权限,则走复制方法返回路径,Android13以上可按需申请图片、视频、音频读取权限,但是针对doc、pdf等其它类型文件无法返回真实路径,除非授予管理所有文件权限
java
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
权限:使用photopicker时,需要Android13以上按需申请以下使用的权限,否则MediaStore不返回真实路径,无权限情况下走复制方法返回路径
java
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO"/>
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO"/>
//Activity中权限申请与回调
ActivityResultLauncher<String[]> permissionLauncher = registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), map->{
//处理权限结果{android.permission.READ_MEDIA_IMAGES=true, android.permission.READ_MEDIA_VIDEO=true}
});
if (!PmUtil.hasStoragePm(this)){
PmUtil.requestStoragePm(permissionLauncher);
}
//权限申请工具类
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Build;
import androidx.activity.result.ActivityResultLauncher;
import androidx.core.content.ContextCompat;
public class PmUtil {
public static boolean hasStoragePm(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
return ContextCompat.checkSelfPermission(context, Manifest.permission.READ_MEDIA_IMAGES) == PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission(context, Manifest.permission.READ_MEDIA_VIDEO) == PackageManager.PERMISSION_GRANTED;
} else {
return ContextCompat.checkSelfPermission(context, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
}
}
public static void requestStoragePm(ActivityResultLauncher<String[]> launcher) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
launcher.launch(new String[]{Manifest.permission.READ_MEDIA_IMAGES, Manifest.permission.READ_MEDIA_VIDEO});
} else {
launcher.launch(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE});
}
}
}
java
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
import android.provider.OpenableColumns;
import android.util.Log;
import androidx.loader.content.CursorLoader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
public class FileUtil {
public static String getFilePathByUri(Context context, Uri uri) {
// 以 file:// 开头的
if (ContentResolver.SCHEME_FILE.equals(uri.getScheme())) {
return uri.getPath();
}
// 以/storage开头的也直接返回
if (isOtherDocument(uri)) {
return uri.getPath();
}
// 版本兼容的获取!
String path;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
path = getFilePathByUri_BELOWAPI11(context, uri);
if (path != null) {
Log.e("cxy", "getFilePathByUri_BELOWAPI11获取到的路径为:" + path);
return path;
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB && Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN_MR2) {
path = getFilePathByUri_API11to18(context, uri);
if (path != null) {
Log.e("cxy", "getFilePathByUri_API11to18获取到的路径为:" + path);
return path;
}
}
path = getFilePathByUri_API19(context, uri);
Log.e("cxy", "getFilePathByUri_API19获取到的路径为:" + path);
return path;
}
private static String getFilePathByUri_BELOWAPI11(Context context, Uri uri) {
// 以 content:// 开头的,比如 content://media/extenral/images/media/17766
if (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
String path = null;
String[] projection = new String[]{MediaStore.Images.Media.DATA};
Cursor cursor = context.getContentResolver().query(uri, projection, null, null, null);
if (cursor != null) {
if (cursor.moveToFirst()) {
int columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
if (columnIndex > -1) {
path = cursor.getString(columnIndex);
}
}
cursor.close();
}
return path;
}
return null;
}
private static String getFilePathByUri_API11to18(Context context, Uri contentUri) {
String[] projection = {MediaStore.Images.Media.DATA};
String result = null;
CursorLoader cursorLoader = new CursorLoader(context, contentUri, projection, null, null, null);
Cursor cursor = cursorLoader.loadInBackground();
if (cursor != null) {
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
result = cursor.getString(column_index);
cursor.close();
}
return result;
}
private static String getFilePathByUri_API19(Context context, Uri uri) {
// 4.4及之后的 是以 content:// 开头的,比如 content://com.android.providers.media.documents/document/image%3A235700
if (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme()) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
if (DocumentsContract.isDocumentUri(context, uri)) {
if (isExternalStorageDocument(uri)) {
// ExternalStorageProvider
String docId = DocumentsContract.getDocumentId(uri);
String[] split = docId.split(":");
String type = split[0];
if ("primary".equalsIgnoreCase(type)) {
if (split.length > 1) {
return Environment.getExternalStorageDirectory() + "/" + split[1];
} else {
return Environment.getExternalStorageDirectory() + "/";
}
// This is for checking SD Card
}
} else if (isDownloadsDocument(uri)) {
//下载内容提供者时应当判断下载管理器是否被禁用
int stateCode = context.getPackageManager().getApplicationEnabledSetting("com.android.providers.downloads");
if (stateCode != 0 && stateCode != 1) {
return null;
}
String id = DocumentsContract.getDocumentId(uri);
// 如果出现这个RAW地址,我们则可以直接返回!
if (id.startsWith("raw:")) {
return id.replaceFirst("raw:", "");
}
if (id.contains(":")) {
String[] tmp = id.split(":");
if (tmp.length > 1) {
id = tmp[1];
}
}
Uri contentUri = Uri.parse("content://downloads/public_downloads");
Log.e("cxy", "测试打印Uri: " + uri);
try {
contentUri = ContentUris.withAppendedId(contentUri, Long.parseLong(id));
} catch (Exception e) {
e.printStackTrace();
}
String path = getDataColumn(context, contentUri, null, null);
if (path != null) return path;
// 兼容某些特殊情况下的文件管理器!
String fileName = getFileNameByUri(context, uri);
if (fileName != null) {
path = Environment.getExternalStorageDirectory().toString() + "/Download/" + fileName;
File pathFile = new File(path);
if (!pathFile.exists()) {
path = getFilePathForCopy(context, uri);
}
return path;
}
} else if (isMediaDocument(uri)) {
// MediaProvider
String docId = DocumentsContract.getDocumentId(uri);
String[] split = docId.split(":");
String type = split[0];
Uri contentUri;
if ("image".equals(type)) {
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
} else if ("video".equals(type)) {
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
} else if ("audio".equals(type)) {
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
} else {
contentUri = MediaStore.Files.getContentUri("external");
}
String selection = "_id=?";
String[] selectionArgs = new String[]{split[1]};
String resultPath = getDataColumn(context, contentUri, selection, selectionArgs);
if (resultPath == null) {
resultPath = getFilePathForCopy(context, uri);
}
return resultPath;
}
} else {
//content://media/picker/0/com.android.providers.media.photopicker/media/1000008424
//photopicker情况,有权限则返回真实路径,无权限则返回复制后的内部路径,但复制后的名称是ID
String uriPath = uri.getPath();
String id = "";
if (uriPath != null) {
String[] strings = uriPath.split("/");
id = strings[strings.length - 1];
}
String selection = "_id=?";
String[] selectionArgs = new String[]{id};
Uri contentUri = MediaStore.Files.getContentUri("external");
String resultPath = getDataColumn(context, contentUri, selection, selectionArgs);
if (resultPath == null) {
resultPath = getFilePathForCopy(context, uri);
}
return resultPath;
}
}
return null;
}
private static String getFileNameByUri(Context context, Uri uri) {
String relativePath = getFileRelativePathByUri_API18(context, uri);
if (relativePath == null) relativePath = "";
final String[] projection = {
MediaStore.MediaColumns.DISPLAY_NAME
};
try (Cursor cursor = context.getContentResolver().query(uri, projection, null, null, null)) {
if (cursor != null && cursor.moveToFirst()) {
int index = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DISPLAY_NAME);
String path = relativePath + cursor.getString(index);
cursor.close();
return path;
}
}
return null;
}
private static String getFileRelativePathByUri_API18(Context context, Uri uri) {
final String[] projection;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
projection = new String[]{
MediaStore.MediaColumns.RELATIVE_PATH
};
try (Cursor cursor = context.getContentResolver().query(uri, projection, null, null, null)) {
if (cursor != null && cursor.moveToFirst()) {
int index = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.RELATIVE_PATH);
String path = cursor.getString(index);
cursor.close();
return path;
}
}
}
return null;
}
private static String getFilePathForCopy(Context context, Uri uri) {
try {
Cursor returnCursor = context.getContentResolver().query(uri, null, null, null, null);
int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
returnCursor.moveToFirst();
String name = (returnCursor.getString(nameIndex));
File file = new File(context.getCacheDir(), name);
InputStream inputStream = context.getContentResolver().openInputStream(uri);
FileOutputStream outputStream = new FileOutputStream(file);
int read = 0;
int maxBufferSize = 1024 * 1024;
int bytesAvailable = inputStream.available();
int bufferSize = Math.min(bytesAvailable, maxBufferSize);
final byte[] buffers = new byte[bufferSize];
while ((read = inputStream.read(buffers)) != -1) {
outputStream.write(buffers, 0, read);
}
returnCursor.close();
inputStream.close();
outputStream.close();
return file.getPath();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) {
final String column = MediaStore.Images.Media.DATA;
final String[] projection = {column};
try (Cursor cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null)) {
if (cursor != null && cursor.moveToFirst()) {
final int column_index = cursor.getColumnIndexOrThrow(column);
String path = cursor.getString(column_index);
cursor.close();
return path;
}
} catch (IllegalArgumentException iae) {
iae.printStackTrace();
}
return null;
}
private static boolean isExternalStorageDocument(Uri uri) {
return "com.android.externalstorage.documents".equals(uri.getAuthority());
}
private static boolean isOtherDocument(Uri uri) {
// 以/storage开头的也直接返回
if (uri != null && uri.getPath() != null) {
String path = uri.getPath();
if (path.startsWith("/storage")) {
return true;
}
if (path.startsWith("/external_files")) {
return true;
}
}
return false;
}
private static boolean isDownloadsDocument(Uri uri) {
return "com.android.providers.downloads.documents".equals(uri.getAuthority());
}
private static boolean isMediaDocument(Uri uri) {
return "com.android.providers.media.documents".equals(uri.getAuthority());
}
//如果是复制的文件,完成后清除缓存
public static void deleteCacheFile(Context context, String path) {
if (path == null) {
return;
}
File file = new File(path);
if (file.exists() && context.getCacheDir().equals(file.getParentFile())) {
Log.e("cxy", "删除成功?" + file.delete());
}
}
}