【发布时间】:2022-08-24 04:51:32
【问题描述】:
对不起,很长的帖子。
我想使用 API 从 Android 将不同类型的文件上传到我们的服务器。
我正在使用以下代码进行文件选择
Intent pickDocument = new Intent(Intent.ACTION_GET_CONTENT);
pickDocument.addCategory(Intent.CATEGORY_OPENABLE);
pickDocument.setType(\"*/*\");
activity.startActivityForResult(Intent.createChooser(pickDocument, \"ChooseFile\"), requestCode);
我正在使用下面的类从 URI 获取文件路径。
public class UriUtils {
private static Uri contentUri = null;
private static MimeTypeMap mimeType = MimeTypeMap.getSingleton();
@SuppressLint(\"NewApi\")
public static String getPathFromUri(final Context context, final Uri uri) {
// check here to is it KITKAT or new version
final boolean isKitKatOrAbove = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
String selection = null;
String[] selectionArgs = null;
// DocumentProvider
if (isKitKatOrAbove && DocumentsContract.isDocumentUri(context, uri)) {
// ExternalStorageProvider
if (isExternalStorageDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(\":\");
final String type = split[0];
String fullPath = getPathFromExtSD(split);
if (fullPath != \"\") {
return fullPath;
} else {
return null;
}
} else if (isDownloadsDocument(uri)) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
final String id;
Cursor cursor = null;
try {
cursor = context.getContentResolver().query(uri, new String[]{MediaStore.MediaColumns.DISPLAY_NAME}, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
String fileName = cursor.getString(0);
String path = Environment.getExternalStorageDirectory().toString() + \"/Download/\" + fileName;
if (!TextUtils.isEmpty(path)) {
return path;
}
}
} finally {
if (cursor != null)
cursor.close();
}
id = DocumentsContract.getDocumentId(uri);
if (!TextUtils.isEmpty(id)) {
if (id.startsWith(\"raw:\")) {
return id.replaceFirst(\"raw:\", \"\");
}
String[] contentUriPrefixesToTry = new String[]{
\"content://downloads/public_downloads\",
\"content://downloads/my_downloads\"
};
for (String contentUriPrefix : contentUriPrefixesToTry) {
try {
final Uri contentUri = ContentUris.withAppendedId(Uri.parse(contentUriPrefix), Long.valueOf(id));
return getDataColumn(context, contentUri, null, null);
} catch (NumberFormatException e) {
//In Android 8 and Android P the id is not a number
return uri.getPath().replaceFirst(\"^/document/raw:\", \"\").replaceFirst(\"^raw:\", \"\");
}
}
}
} else {
final String id = DocumentsContract.getDocumentId(uri);
if (id.startsWith(\"raw:\")) {
return id.replaceFirst(\"raw:\", \"\");
}
try {
contentUri = ContentUris.withAppendedId(
Uri.parse(\"content://downloads/public_downloads\"), Long.valueOf(id));
} catch (NumberFormatException e) {
e.printStackTrace();
}
if (contentUri != null) {
return getDataColumn(context, contentUri, null, null);
}
}
} else if (isMediaDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(\":\");
final String type = split[0];
selection = \"_id=?\";
selectionArgs = new String[]{split[1]};
Uri contentUri = null;
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;
}
return getDataColumn(context, contentUri, selection, selectionArgs);
} else if (isGoogleDriveUri(uri)) {
return getDriveFilePath(uri, context);
}
} else if (\"content\".equalsIgnoreCase(uri.getScheme())) {
if (isGooglePhotosUri(uri)) {
return uri.getLastPathSegment();
}
if (isGoogleDriveUri(uri)) {
return getDriveFilePath(uri, context);
}
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.N) {
return getMediaFilePathForN(uri, context);
} else {
return copyFromSource(uri, context);
}
} else if (\"file\".equalsIgnoreCase(uri.getScheme())) {
return uri.getPath();
}
return null;
}
private static boolean fileExists(String filePath) {
File file = new File(filePath);
return file.exists();
}
private static String getPathFromExtSD(String[] pathData) {
final String type = pathData[0];
final String relativePath = \"/\" + pathData[1];
String fullPath = \"\";
if (\"primary\".equalsIgnoreCase(type)) {
fullPath = Environment.getExternalStorageDirectory() + relativePath;
if (fileExists(fullPath)) {
return fullPath;
}
}
fullPath = System.getenv(\"SECONDARY_STORAGE\") + relativePath;
if (fileExists(fullPath)) {
return fullPath;
}
fullPath = System.getenv(\"EXTERNAL_STORAGE\") + relativePath;
if (fileExists(fullPath)) {
return fullPath;
}
return fullPath;
}
private static String getDriveFilePath(Uri uri, Context context) {
Uri returnUri = uri;
Cursor returnCursor = context.getContentResolver().query(returnUri, null, null, null, null);
int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE);
returnCursor.moveToFirst();
String name = (returnCursor.getString(nameIndex));
String size = (Long.toString(returnCursor.getLong(sizeIndex)));
File file = new File(context.getCacheDir(), name);
try {
InputStream inputStream = context.getContentResolver().openInputStream(uri);
FileOutputStream outputStream = new FileOutputStream(file);
int read = 0;
int maxBufferSize = 1 * 1024 * 1024;
int bytesAvailable = inputStream.available();
//int bufferSize = 1024;
int bufferSize = Math.min(bytesAvailable, maxBufferSize);
final byte[] buffers = new byte[bufferSize];
while ((read = inputStream.read(buffers)) != -1) {
outputStream.write(buffers, 0, read);
}
inputStream.close();
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
return file.getPath();
}
private static String getMediaFilePathForN(Uri uri, Context context) {
Uri returnUri = uri;
Cursor returnCursor = context.getContentResolver().query(returnUri, null, null, null, null);
int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE);
returnCursor.moveToFirst();
String name = (returnCursor.getString(nameIndex));
String size = (Long.toString(returnCursor.getLong(sizeIndex)));
File file = new File(context.getFilesDir(), name);
try {
InputStream inputStream = context.getContentResolver().openInputStream(uri);
FileOutputStream outputStream = new FileOutputStream(file);
int read = 0;
int maxBufferSize = 1 * 1024 * 1024;
int bytesAvailable = inputStream.available();
//int bufferSize = 1024;
int bufferSize = Math.min(bytesAvailable, maxBufferSize);
final byte[] buffers = new byte[bufferSize];
while ((read = inputStream.read(buffers)) != -1) {
outputStream.write(buffers, 0, read);
}
inputStream.close();
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
return file.getPath();
}
private static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) {
Cursor cursor = null;
final String column = \"_data\";
final String[] projection = {column};
try {
cursor = context.getContentResolver().query(uri, projection,
selection, selectionArgs, null);
if (cursor != null && cursor.moveToFirst()) {
final int index = cursor.getColumnIndexOrThrow(column);
return cursor.getString(index);
}
} finally {
if (cursor != null)
cursor.close();
}
return null;
}
private static String copyFromSource(Uri uri, Context context) {
ContentResolver contentResolver = context.getContentResolver();
String fileExtension = getFileExtension(uri, contentResolver);
String fileName = queryName(uri, contentResolver);
if (fileName == null)
fileName = getFileName(fileExtension);
// the file which will be the new cached file
File filePath = context.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS);
File outputFile = new File(filePath, fileName);
try {
InputStream inputStream = context.getContentResolver().openInputStream(uri);
FileOutputStream outputStream = new FileOutputStream(outputFile);
int read = 0;
int maxBufferSize = 1 * 1024 * 1024;
int bytesAvailable = inputStream.available();
//int bufferSize = 1024;
int bufferSize = Math.min(bytesAvailable, maxBufferSize);
final byte[] buffers = new byte[bufferSize];
while ((read = inputStream.read(buffers)) != -1) {
outputStream.write(buffers, 0, read);
}
inputStream.close();
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
return outputFile.getAbsolutePath();
}
@SuppressLint(\"Recycle\")
private static String queryName(Uri uri, ContentResolver contentResolver) {
Cursor returnCursor = contentResolver.query(uri, null, null, null, null);
if (returnCursor == null)
return null;
int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
if (nameIndex == -1)
return null;
returnCursor.moveToFirst();
String name = returnCursor.getString(nameIndex);
returnCursor.close();
return name;
}
private static String getFileExtension(Uri uri, ContentResolver contentResolver) {
return mimeType.getExtensionFromMimeType(contentResolver.getType(uri));
}
private static String getFileName(String fileExtension) {
return System.currentTimeMillis() + fileExtension + \"\";
}
private static boolean isExternalStorageDocument(Uri uri) {
return \"com.android.externalstorage.documents\".equals(uri.getAuthority());
}
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());
}
private static boolean isGooglePhotosUri(Uri uri) {
return \"com.google.android.apps.photos.content\".equals(uri.getAuthority());
}
private static boolean isGoogleDriveUri(Uri uri) {
return \"com.google.android.apps.docs.storage\".equals(uri.getAuthority()) || \"com.google.android.apps.docs.storage.legacy\".equals(uri.getAuthority());
}
}
此代码工作正常安卓 10及以下。但我在 Android 12 中遇到问题
如果我选择一个文件文档(doc、pdf、excel、txt)除了图像、视频和音频,我得到路径空.
在 Android 12(Pixel 3)中,我得到了低于 URI
content://com.android.providers.media.documents/document/document%3A27958
所以根据上面的代码, 这是媒体文件.
private static boolean isMediaDocument(Uri uri) {
return \"com.android.providers.media.documents\".equals(uri.getAuthority());
}
如果文件是媒体,我只处理 3 种类型的文件
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;
}
如果有文档文件(doc、excel、pdf、txt),如何获取 contentUri?
-
\"我正在使用下面的类从 URI 获取文件路径\" - 删除它。 \"如果我选择的文件是除图像、视频和音频之外的文档(doc、pdf、excel、txt),我的路径为空。\" - 你会因为很多
Uri值而失败,包括比 12 更早的 Android 版本。Uri不是文件。有很多Uri值的来源不会映射到文件系统路径,更不用说您可以使用的文件系统路径了。所选内容可能在 Google Drive 或其他云存储提供商中,或在网络文件服务器上,或在加密文档存储中等。 -
我知道你会添加你的输入@CommonsWare :)。我花了一个多星期的时间在这上面。如果我删除您提到的上述内容。如何为所有 Android 版本和所有类型的文件(即图像、音频、视频和其他文件)获取正确的文件路径?
-
\"如何为所有 Android 版本获取正确的文件路径\" - 你不知道。尽可能按照设计使用
Uri,例如通过ContentResolver上的openInputStream()/openOutputStream()。对于流不足的情况(例如,SQLite 数据库),您需要制作内容的副本。您的问题中隐藏了代码,但是制作内容的副本也具有用户体验影响(例如,导入/导出或备份/恢复语义)。
标签: android android-file