【发布时间】:2022-08-04 17:13:01
【问题描述】:
我一直在尝试使用多部分 API 将文件上传到服务器。为此,我正在触发一个隐式意图,以便用户可以从设备的存储中选择一个文件。
在onActivityResult 中,我执行以下步骤:
- 从
Intent对象中获取Uri - 从获取的
Uri获取文件路径 - 从文件路径创建
File对象 - 调用API并上传文件
现在我在步骤 2 中获取 pdf 文件路径时面临挑战。我浏览了很多文章,发现了一段代码,每个人都共享但不适用于所有设备。这是sn-p:
public class FileUtils { private static Uri contentUri = null; /** * Get a file path from a Uri. This will get the the path for Storage Access * Framework Documents, as well as the _data field for the MediaStore and * other file-based ContentProviders.<br> * <br> * * @param context The context. * @param uri The Uri to query. */ @SuppressLint(\"NewApi\") public static String getPath(final Context context, final Uri uri) { // check here to KITKAT or new version final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; String selection = null; String[] selectionArgs = null; // DocumentProvider if (isKitKat && 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; } } // DownloadsProvider 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)); /* final Uri contentUri = ContentUris.withAppendedId( Uri.parse(\"content://downloads/public_downloads\"), 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); final boolean isOreo = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O; 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); } } } // MediaProvider else if (isMediaDocument(uri)) { final String docId = DocumentsContract.getDocumentId(uri); final String[] split = docId.split(\":\"); final String type = split[0]; 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; } selection = \"_id=?\"; selectionArgs = new String[]{split[1]}; return getDataColumn(context, contentUri, selection, selectionArgs); } else if (isGoogleDriveUri(uri)) { return getDriveFilePath(uri, context); } } // MediaStore (and general) 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 getFilePathFromURI(context,uri); return getMediaFilePathForN(uri, context); // return getRealPathFromURI(context,uri); }else { return getDataColumn(context, uri, null, null); } } // File else if (\"file\".equalsIgnoreCase(uri.getScheme())) { return uri.getPath(); } return null; } /** * Check if a file exists on device * * @param filePath The absolute file path */ private static boolean fileExists(String filePath) { File file = new File(filePath); return file.exists(); } /** * Get full file path from external storage * * @param pathData The storage type and the relative path */ private static String getPathFromExtSD(String[] pathData) { final String type = pathData[0]; final String relativePath = \"/\" + pathData[1]; String fullPath = \"\"; // on my Sony devices (4.4.4 & 5.1.1), `type` is a dynamic string // something like \"71F8-2C0A\", some kind of unique id per storage // don\'t know any API that can get the root path of that storage based on its id. // // so no \"primary\" type, but let the check here for other devices if (\"primary\".equalsIgnoreCase(type)) { fullPath = Environment.getExternalStorageDirectory() + relativePath; if (fileExists(fullPath)) { return fullPath; } } // Environment.isExternalStorageRemovable() is `true` for external and internal storage // so we cannot relay on it. // // instead, for each possible path, check if file exists // we\'ll start with secondary storage as this could be our (physically) removable sd card 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); /* * Get the column indexes of the data in the Cursor, * * move to the first row in the Cursor, get the data, * * and display it. * */ 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); } Log.e(\"File Size\", \"Size \" + file.length()); inputStream.close(); outputStream.close(); Log.e(\"File Path\", \"Path \" + file.getPath()); Log.e(\"File Size\", \"Size \" + file.length()); } catch (Exception e) { Log.e(\"Exception\", e.getMessage()); } return file.getPath(); } private static String getMediaFilePathForN(Uri uri, Context context) { Uri returnUri = uri; Cursor returnCursor = context.getContentResolver().query(returnUri, null, null, null, null); /* * Get the column indexes of the data in the Cursor, * * move to the first row in the Cursor, get the data, * * and display it. * */ 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); } Log.e(\"File Size\", \"Size \" + file.length()); inputStream.close(); outputStream.close(); Log.e(\"File Path\", \"Path \" + file.getPath()); Log.e(\"File Size\", \"Size \" + file.length()); } catch (Exception e) { Log.e(\"Exception\", e.getMessage()); } 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; } /** * @param uri - The Uri to check. * @return - Whether the Uri authority is ExternalStorageProvider. */ private static boolean isExternalStorageDocument(Uri uri) { return \"com.android.externalstorage.documents\".equals(uri.getAuthority()); } /** * @param uri - The Uri to check. * @return - Whether the Uri authority is DownloadsProvider. */ private static boolean isDownloadsDocument(Uri uri) { return \"com.android.providers.downloads.documents\".equals(uri.getAuthority()); } /** * @param uri - The Uri to check. * @return - Whether the Uri authority is MediaProvider. */ private static boolean isMediaDocument(Uri uri) { return \"com.android.providers.media.documents\".equals(uri.getAuthority()); } /** * @param uri - The Uri to check. * @return - Whether the Uri authority is Google Photos. */ private static boolean isGooglePhotosUri(Uri uri) { return \"com.google.android.apps.photos.content\".equals(uri.getAuthority()); } /** * @param uri The Uri to check. * @return Whether the Uri authority is Google Drive. */ 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()); } }但是这段代码并不适用于所有设备。它尝试使用内容解析器从
_data列(存储实际文件路径)中获取内容。经过一番尝试和尝试,我能够通过在下面的代码中添加一个案例来获取更多设备上的路径:
else if (isMediaDocument(uri)){ ... 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; } // Below case is the new addition else if (\"document\".equals(type)) { contentUri = MediaStore.getMediaUri(context, uri); } ... }为了解决所有设备,我挖得更深了。在进一步调试并获取所有列后,我知道这个
_data在某些设备中不存在,而这就是它失败并导致崩溃的地方。java.lang.IllegalStateException: java.io.FileNotFoundException: No root for document在这行代码上contentUri = MediaStore.getMediaUri(context, uri);寻找解决所有设备的解决方案。谢谢 !
-
我建议您使用
ContentResolver获取文件内容,将其复制到缓存中的临时文件,然后上传该文件。Uris 有很多方案和可能性,考虑每种情况几乎是不可能的。如果您需要从Uri获取内容的帮助,请查看this 答案。 -
同意!在这里感谢您的时间和投入。当您说缓存时,您的意思是 getExternalCacheDir() 路径吗?
-
不一定是外部的,可以是正常的
cacheDir
标签: android