【问题标题】:How to convert a content Uri into a File如何将内容 Uri 转换为文件
【发布时间】:2023-04-06 15:36:01
【问题描述】:

我知道关于这个确切的主题有很多问题,但是在花了两天时间阅读和尝试之后,没有一个可以解决我的问题。

这是我的代码:

我在我的onCreate() 中启动ACTION_GET_CONTENT

Intent selectIntent = new Intent(Intent.ACTION_GET_CONTENT);
        selectIntent.setType("audio/*");
        startActivityForResult(selectIntent, AUDIO_REQUEST_CODE);

在onActivityResult()中检索Uri

@Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == AUDIO_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
            if ((data != null) && (data.getData() != null)) {
                audio = data.getData();
            }
        }
    }

将Uri 传递给另一个活动并检索它

Intent debugIntent = new Intent(this, Debug.class);
            Bundle bundle = new Bundle();
            bundle.putString("audio", audio.toString());
            debugIntent.putExtras(bundle);
            startActivity(debugIntent);

Intent intent = this.getIntent();
        Bundle bundle = intent.getExtras();
        audio = Uri.parse((String) bundle.get("audio"));

我已经根据另一个 SO 答案实现了这个方法。获取 Uri 的实际路径

public static String getRealPathFromUri(Activity activity, Uri contentUri) {
        String[] proj = { MediaStore.Audio.Media.DATA };
        Cursor cursor = activity.managedQuery(contentUri, proj, null, null, null);
        int column_index = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA);
        cursor.moveToFirst();
        return cursor.getString(column_index);
    }

并在调试活动的onCreate() 中尝试生成文件:

File audioFile = new File(getRealPathFromUri(this, audio));

这是错误的样子:

原因:java.lang.NullPointerException 在 java.io.File.(File.java:262) 在 com.dancam.lietome.Debug.onCreate(Debug.java:35)

当我运行应用程序时,我在最后一行得到一个 NPE。音频 Uri 不是 NULL,所以我不明白它是由什么引起的。

如果你能帮助我,我将不胜感激。

This is the library我正在努力合作。

注意:我确切地知道 NPE 是什么,但即使调试我也无法从这种特定情况下的原因中弄清楚。

【问题讨论】:

  • @isabsent 我没有看到任何结果我得到了 NPE
  • 我明白了。在方法getRealPathFromUri(Activity activity, Uri contentUri) 中为每个字符串设置断点并找出导致NPE 的字符串。我想cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA) 会抛出 NPE。
  • 您需要File 做什么?你有一个Uri,就用它,不,不需要传递String,改用Intent#setData
  • 只需使用ContentResolver API - 它具有打开任何基于content:// 的Uri 的方法
  • 然后尝试阅读ContentResolver 文档,但您没有回答我的问题:您想在哪里使用您的音频Uri?你有机会可以更轻松地做到这一点......

标签: android file uri


【解决方案1】:

将 Uri 传递给另一个活动并检索它

您的其他活动不一定有权使用Uri 标识的内容。将FLAG_GRANT_READ_URI_PERMISSION 添加到用于启动该活动的Intent,并通过Intent (setData()) 的“数据”方面传递Uri,而不是额外的。

获取 Uri 的实际路径

首先,您返回的Uri 不要求来自MediaStore。

其次,managedQuery() 已被弃用 六年。

第三,MediaStore 拥有的路径不要求是您可以使用的路径。例如,音频文件可能位于可移动存储上,虽然 MediaStore 可以访问它,但您不能。

如何将内容 Uri 转换为文件

在后台线程上:

  • 通过在Context 上调用getContentResolver() 来获取ContentResolver
  • 在ContentResolver上调用openInputStream(),传入你从ACTION_GET_CONTENT得到的Uri,得到InputStream在Uri标识的内容上
  • 在一些 File 上创建一个 FileOutputStream,您希望在其中存储内容
  • 使用 Java I/O 将内容从 InputStream 复制到 FileOutputStream,完成后关闭两个流

【讨论】:

  • 但老实说,我真的怀疑 OP 是否需要 File - 大多数第 3 方库当他们有 File 参数时,他们也接受 InputStream 作为他们的输入 - 幸运的是 OP 不想说他在哪里想用它...
  • @pskink 请检查已编辑问题中的库。那里没有输入流构造函数,只有一个获取路径和名称,另一个获取整个文件。我正在考虑使用将文件作为参数的那个。并感谢 CommonsWare 的出色而详细的回答
  • @Daniele 你看到mInFile 用在哪里了吗?只有一个地方:FileInputStream fileStream = new FileInputStream(mInFile);,所以最简单的解决方案就是添加一个新的构造函数,它采用InputStream
  • 好的,最终它是一个InputStream。我只需要修改构造函数对吗?进而?我将什么作为 inputStream 传递? Uri 本身?
  • @Daniele 请阅读 CommonsWare 答案 - 他说如何从您的 Uri 获取 InputStream
【解决方案2】:

我在 Android Q 上遇到了同样的问题,所以我最终创建了一个新文件并使用来自内容的输入流来填充该文件 这是我在 kotlin 中的操作方式:

private var pdfFile: File? = null

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        if (resultCode == Activity.RESULT_OK) {
            if (data != null) {
                when (requestCode) {
                    REQUEST_CODE_DOC -> {
                        data.data?.let {
                            if (it.scheme.equals("content")) {
                                val pdfBytes =
                                    (contentResolver?.openInputStream(it))?.readBytes()
                                pdfFile = File(
                                    getExternalFilesDir(null),
                                    "Lesson ${Calendar.getInstance().time}t.pdf"
                                )

                                if (pdfFile!!.exists())
                                    pdfFile!!.delete()
                                try {
                                    val fos = FileOutputStream(pdfFile!!.path)
                                    fos.write(pdfBytes)
                                    fos.close()
                                } catch (e: Exception) {
                                    Timber.e("PDF File", "Exception in pdf callback", e)
                                }
                            } else {
                                pdfFile = it.toFile()
                            }

                        }
                    }
                }
            }
        }
    }

【讨论】:

  • 您好,如果 pdf 很大,我认为 pdfBytes 对于程序内存来​​说会非常大。你怎么看?
【解决方案3】:

Daniele,您可以在 onActivityResult() 中直接从如下数据中获取文件路径:

String gilePath = data.getData().getPath();

【讨论】:

  • 我已检查此代码是否为 .doc 文件格式,我从中获得了真实路径。
  • 它返回 home:test1.wav 这不是整个路径,对吧?因为文件没有正确生成
  • 你在模拟器还是真机上测试这个?
  • 我也在 OnePlus 3t 中使用此代码,我得到了正确的路径。
  • 这只有在方案是 file 时才有效,这种情况越来越少见。
猜你喜欢
  • 1970-01-01
  • 2011-11-10
  • 2014-08-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-15
  • 2013-07-31
  • 2021-04-03
相关资源
最近更新 更多