Android用ACTION_GET_CONTENT打开一个文件导致不同的Uri

我试图使用Intent.ACTION_GET_CONTENT打开文件。 Android文件浏览器

根据Android版本/设备品牌,文件浏览器打开,我得到以下结果:

Downloads选择文件:

 content://com.android.providers.downloads.documents/document/446 

Fotos选择一个文件:

 content://media/external/images/media/309 

FileCommander选择一个文件:

 file:///storage/emulated/0/DCIM/Camera/20141027_132114.jpg 

我可以打开所有这些文件,除非我尝试从Downloads,AudioAfbeeldingen (图像)打开文件

我可能无法处理这种Uri: content://com.android.providers.downloads.documents/document/446

我尝试过以下的事情:

  • 尝试通过新文件(uri.getPath())打开文件。 File.exists()返回false。
  • 尝试通过getContext()。getContentResolver()。openInputStream(uri)打开/到达文件。 结果进入FileNotFoundException
  • 尝试使用以下代码打开文件:

     public static String getRealPathFromURI_API19(Context context, Uri uri) { Log.i("uri", uri.getPath()); String filePath = ""; if (uri.getScheme().equals("file")) { return uri.getPath(); } else if (DocumentsContract.isDocumentUri(context, uri)) { String wholeID = DocumentsContract.getDocumentId(uri); Log.i("wholeID", wholeID); // Split at colon, use second item in the array String[] splits = wholeID.split(":"); if (splits.length == 2) { String id = splits[1]; String[] column = {MediaStore.Images.Media.DATA}; // where id is equal to String sel = MediaStore.Images.Media._ID + "=?"; Cursor cursor = context.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, column, sel, new String[]{id}, null); int columnIndex = cursor.getColumnIndex(column[0]); if (cursor.moveToFirst()) { filePath = cursor.getString(columnIndex); } cursor.close(); } } else { filePath = AttachmentUtils.getPath(context, uri); } return filePath; } 

我究竟做错了什么?

更新:我注意到屏幕截图中列出的文件中没有实际存在的文件。 我使用的设备是包含垃圾数据的公司平板电脑。 我的同事告诉我,此设备曾与其他Google帐户关联。 这些文件可能是以前帐户中不再存在/可访问的文件。

我自己的结论是我在Android中遇到了一些错误。 我的错误报告

2017年2月6日更新:

Android禁止使用file:// URI。 请考虑考虑一下。

禁止存档:Uri Scheme到目前为止,Android 7.0最大的兼容性问题是Uri值的文件:scheme实际上是禁止的。 如果你试图传递一个文件:Uri在一个转到另一个应用程序的Intent中 – 无论是通过额外的还是作为Intent的“数据”方面 – 你将崩溃并发生FileUriExposedExceptionexception。 将文件放入类似的问题:剪贴板中的剪贴板上的Uri值。 这来自StrictMode的更新版本。 StrictMode.VmPolicy.Builder有一个penaltyDeathOnFileUriExposure()方法,它触发文件的检测:Uri值和生成的FileUriExposedExceptionexception。 并且,它似乎是预配置的,就像StrictMode预先配置为应用penaltyDeathOnNetwork()(NetworkOnMainThreadException的崩溃源)一样。

使用下面的代码。这肯定会起作用。

 public static String getPath(Context context, Uri uri) { final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; // 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]; if ("primary".equalsIgnoreCase(type)) { return Environment.getExternalStorageDirectory() + "/" + split[1]; } // TODO handle non-primary volumes } // DownloadsProvider else if (isDownloadsDocument(uri)) { final String id = DocumentsContract.getDocumentId(uri); final Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(id)); 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; } final String selection = "_id=?"; final String[] selectionArgs = new String[] {split[1]}; return getDataColumn(context, contentUri, selection, selectionArgs); } } // MediaStore (and general) else if ("content".equalsIgnoreCase(uri.getScheme())) { // Return the remote address if (isGooglePhotosUri(uri)) return uri.getLastPathSegment(); return getDataColumn(context, uri, null, null); } // File else if ("file".equalsIgnoreCase(uri.getScheme())) { return uri.getPath(); } return null; } public 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; } public 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. */ public 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. */ public 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. */ public static boolean isGooglePhotosUri(Uri uri) { return "com.google.android.apps.photos.content".equals(uri.getAuthority()); } 

使用以下代码以任何格式浏览文件。

 public void browseClick() { Intent intent = new Intent(Intent.ACTION_GET_CONTENT); intent.setType("*/*"); intent.addCategory(Intent.CATEGORY_OPENABLE); //intent.putExtra("browseCoa", itemToBrowse); //Intent chooser = Intent.createChooser(intent, "Select a File to Upload"); try { //startActivityForResult(chooser, FILE_SELECT_CODE); startActivityForResult(Intent.createChooser(intent, "Select a File to Upload"),FILE_SELECT_CODE); } catch (Exception ex) { System.out.println("browseClick :"+ex);//android.content.ActivityNotFoundException ex } } 

然后在onActivityResult中获取该文件路径,如下所示。

 @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == FILE_SELECT_CODE) { if (resultCode == RESULT_OK) { try { Uri uri = data.getData(); if (filesize >= FILE_SIZE_LIMIT) { Toast.makeText(this,"The selected file is too large. Selet a new file with size less than 2mb",Toast.LENGTH_LONG).show(); } else { String mimeType = getContentResolver().getType(uri); if (mimeType == null) { String path = getPath(this, uri); if (path == null) { filename = FilenameUtils.getName(uri.toString()); } else { File file = new File(path); filename = file.getName(); } } else { Uri returnUri = data.getData(); Cursor returnCursor = getContentResolver().query(returnUri, null, null, null, null); int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME); int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE); returnCursor.moveToFirst(); filename = returnCursor.getString(nameIndex); String size = Long.toString(returnCursor.getLong(sizeIndex)); } File fileSave = getExternalFilesDir(null); String sourcePath = getExternalFilesDir(null).toString(); try { copyFileStream(new File(sourcePath + "/" + filename), uri,this); } catch (Exception e) { e.printStackTrace(); } } } catch (Exception e) { e.printStackTrace(); } } } } private void copyFileStream(File dest, Uri uri, Context context) throws IOException { InputStream is = null; OutputStream os = null; try { is = context.getContentResolver().openInputStream(uri); os = new FileOutputStream(dest); byte[] buffer = new byte[1024]; int length; while ((length = is.read(buffer)) > 0) { os.write(buffer, 0, length); } } catch (Exception e) { e.printStackTrace(); } finally { is.close(); os.close(); } } 

在此之后,您可以从应用程序外部存储中打开此文件,并使用适当的操作保存文件。