当前位置: 首页 > 知识库问答 >
问题:

Android打开带有ACTION_GET_CONTENT的文件会导致不同的Uri

华佐
2023-03-14

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

下载中选择文件

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

从<code>Fotos</code>中选择文件:

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

从<code>FileCommander</code>中选择文件:

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

我可以打开所有这些文件,除非我尝试从< code >下载、、< code >音频、< code>Afbeeldingen(图像)中打开文件

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

我尝试了以下方法:

>

  • 尝试通过 new File(uri.getPath()) 打开该文件。File.exists() 返回 false。
  • 尝试通过 getContext().getContentResolver().openInputStream(uri) 打开/访问该文件。结果到文件NotFoundException
  • 尝试使用以下代码打开该文件:

    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中遇到了一些bug。我的错误报告

    Android禁止了file://URI。请考虑考虑。

    禁止文件:Uri方案到目前为止与Android 7.0最大的兼容性问题是,实际上禁止使用文件:Uri值方案。如果您试图将意图中的文件:Uri传递给另一个应用程序-无论是通过额外的还是作为意图的“数据”方面-您将崩溃,并出现FileUriExposedException异常。在将文件:Uri值放在ClipData的剪贴板上时,您将面临类似的问题。这来自StrictMode.StrictModel.VmPolicy的更新版本。生成器有一个PenaltyDeathConfigureExposure()方法,该方法触发对file:Uri值的检测以及由此产生的FileUriExposeException异常。而且,这似乎是预配置的,就像StrictMode预配置为应用penaltyDeathOnNetwork()(NetworkOnMainThreadException崩溃的源)一样。

  • 共有3个答案

    艾安和
    2023-03-14

    kotlin 上接受的答案

    @Suppress("SpellCheckingInspection")
    object PathCompat {
    
        @WorkerThread
        fun getFilePath(context: Context, uri: Uri): String? = context.run {
            return try {
                when {
                    Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT -> getDataColumn(uri, null, null)
                    else -> getPathKitkatPlus(uri)
                }
            } catch (e: Throwable) {
                Timber.e(e)
                null
            }
        }
    
        @Suppress("DEPRECATION")
        @SuppressLint("NewApi", "DefaultLocale")
        private fun Context.getPathKitkatPlus(uri: Uri): String? {
            when {
                DocumentsContract.isDocumentUri(applicationContext, uri) -> {
                    val docId = DocumentsContract.getDocumentId(uri)
                    when {
                        uri.isExternalStorageDocument -> {
                            val parts = docId.split(":")
                            if ("primary".equals(parts[0], true)) {
                                return "${Environment.getExternalStorageDirectory()}/${parts[1]}"
                            }
                        }
                        uri.isDownloadsDocument -> {
                            val contentUri = ContentUris.withAppendedId(
                                Uri.parse("content://downloads/public_downloads"),
                                docId.toLong()
                            )
                            return getDataColumn(contentUri, null, null)
                        }
                        uri.isMediaDocument -> {
                            val parts = docId.split(":")
                            val contentUri = when (parts[0].toLowerCase()) {
                                "image" -> MediaStore.Images.Media.EXTERNAL_CONTENT_URI
                                "video" -> MediaStore.Video.Media.EXTERNAL_CONTENT_URI
                                "audio" -> MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
                                else -> return null
                            }
                            return getDataColumn(contentUri, "_id=?", arrayOf(parts[1]))
                        }
                    }
                }
                "content".equals(uri.scheme, true) -> {
                    return if (uri.isGooglePhotosUri) {
                        uri.lastPathSegment
                    } else {
                        getDataColumn(uri, null, null)
                    }
                }
                "file".equals(uri.scheme, true) -> {
                    return uri.path
                }
            }
            return null
        }
    
        private fun Context.getDataColumn(uri: Uri, selection: String?, args: Array<String>?): String? {
            contentResolver?.query(uri, arrayOf("_data"), selection, args, null)?.use {
                if (it.moveToFirst()) {
                    return it.getString(it.getColumnIndexOrThrow("_data"))
                }
            }
            return null
        }
    
        private val Uri.isExternalStorageDocument: Boolean
            get() = authority == "com.android.externalstorage.documents"
    
        private val Uri.isDownloadsDocument: Boolean
            get() = authority == "com.android.providers.downloads.documents"
    
        private val Uri.isMediaDocument: Boolean
            get() = authority == "com.android.providers.media.documents"
    
        private val Uri.isGooglePhotosUri: Boolean
            get() = authority == "com.google.android.apps.photos.content"
    }
    
    端木志诚
    2023-03-14

    使用系统文件选取器选取任何文件:

    val intent = Intent(Intent.ACTION_GET_CONTENT)
    intent.type = "*/*"
    startActivityForResult(intent, 1)
    

    onActivityResult:

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (requestCode == 1 && resultCode == Activity.RESULT_OK) {
            data?.data?.let {
                getFileFromUri(requireContext().contentResolver, uri, requireContext().cacheDir)
            }
        }
    }
    

    获取文件:

    private fun getFileFromUri(contentResolver: ContentResolver, uri: Uri, directory: File): File {
        val file =
            File.createTempFile("suffix", "prefix", directory)
        file.outputStream().use {
            contentResolver.openInputStream(uri)?.copyTo(it)
        }
    
        return file
    }
    
    贺劲
    2023-03-14

    使用以下代码。这肯定会成功的。

    public static String getPath(Context context, Uri uri) {
    
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            // DocumentProvider
            if (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();
        }
    }
    

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

     类似资料:
    • 我已经安装了 Pug(前玉)插件并添加了一个新的文件模板 创建新的.pug文件时,PhpStorm会创建一个我无法打开的文件。 我已经按照官方页面上的说明进行操作,但它没有帮助。 我错过了哪一步?任何帮助都是巨大的! 编辑: 我已经卸载了Pug插件,使缓存失效并重启。然后删除了我已经创建的模板,因为pstorm创建了一个名为“Pug/Jade File”的新模板。然后,我从上下文菜单中创建了一个新

    • 问题内容: 考虑这两个python程序: : : 运行。在运行时,启动。两者都将愉快地运行,但是如果当前通过打开文件,则会输出一个空字符串。 我期望会引发异常,告诉我该文件已经打开,但是没有发生,而是文件看起来是空的。为什么会这样?检查它是否已被另一个进程打开的正确方法是什么?可以简单地检查是否返回了空字符串,然后重试直到读取其他内容,或者还有其他更Python化的方法吗? 问题答案: 有关在Py

    • 我有一个填充了应该包含一些对象的文本。图像可能比正常线高,这会导致以下问题: 如果图像是一行的最后一个对象,则以下行的高度正确 如果最后一个对象不是图像,则以下行的高度设置为包含图像的行的高度 更有趣的是,如果文本中有一个新的行字符,那么从那一点开始,行的高度也是好的。 只是一个非常基本的: (位于中,后者位于中。)

    • 我正在编写一个概念的演示证明,将电子签名添加到现有的pdf中。我遇到了一个奇怪的问题,我不明白。似乎将签名添加到某些文档可以正常工作,而将签名添加到其他文档则不行,并且会生成Adobe Reader无法打开的损坏文件。 这是我的代码: 请看这里的文件。Infile1.pdf我不能签名,但Infile2.pdf签名很好。Outfile1.pdf是损坏的文件。https://app.box.com/s

    • 音乐作者:denny schneidemesser(by-nc-nd) 知道这是怎么回事吗?

    • 我正试图用reactJS下载一个xlsx文件,但当我在下载后试图打开我的文件时,我收到了这条消息: “Excel无法打开文件‘file.xlsx’,因为文件格式或文件扩展名无效。请验证文件是否已损坏,以及文件扩展名是否与文件格式匹配。” 这是前端代码: 为什么我得到这个错误?请谁来帮帮我,我被困在这个3周 [编辑1] 我尝试下载的文件是在后端构建的,基本上我获取数据库上的值并使用Apache po