相关文章推荐
神勇威武的泡面  ·  Web API 实现 - Best ...·  2 月前    · 
玩篮球的西装  ·  HttpClientJsonExtensio ...·  1 月前    · 
知识渊博的豆芽  ·  nginx正向代理-内网服务器通过代理服务器 ...·  11 月前    · 
另类的滑板  ·  C# 怎样知道List<string>里 ...·  1 年前    · 
严肃的橡皮擦  ·  [后端]gitlab之gitlab-ci自动 ...·  1 年前    · 
气宇轩昂的葫芦  ·  Group By in Java 8 - ...·  1 年前    · 
旅行中的凉茶  ·  oracle pl sql split ...·  2 年前    · 
Code  ›  安卓: FileProvider.getUriForFile读取MediaStore生成的文件?开发者社区
uri
https://cloud.tencent.com/developer/ask/sof/106703294
失恋的充电器
1 年前
首页
学习
活动
专区
工具
TVP
返回腾讯云官网
提问
问 安卓: FileProvider.getUriForFile读取MediaStore生成的文件?
Stack Overflow用户
提问于 2022-02-15 05:06:56
EN

TL;DR

我想获得(读取/生成)文件 Uri --路径类似于下面的路径-- FileProvider ,但不知道如何获得:

val file = File("/external/file/9359") // how do I get its Uri then?

我只知道 FileProvider.getUriForFile 方法,但是它会抛出异常。

我在做什么

我试图模拟下载过程--在 Download 文件夹中创建一个文件,然后将它传递给 ShareSheet ,让用户做它想做的任何事情。

我所做的:

  1. 在 Download 中使用 MediaStore 和 ContentResolver 创建文件。
  2. 有一个 ShareSheet util函数。
  3. 注册 FileProvider 和 filepath.xml 。

在我的例子中,我想通过 ShareSheet 函数共享文件,这需要

  • File 的Uri

但是通常的 file.toUri() 会在SDK 29之上抛出异常。因此,我将其改为 FileProvider.getUriForFile() ,这是谷歌官方推荐的。

直接问题码

val fileUri: Uri = FileProvider.getUriForFile(context, "my.provider", file)

将引发异常:

java.lang.IllegalArgumentException: Failed to find configured root that contains /external/file/9359

我的文件创建代码

val fileUri: Uri? = ContentValues().apply {
    put(MediaStore.MediaColumns.DISPLAY_NAME, "test.txt")
    put(MediaStore.MediaColumns.MIME_TYPE, "text/plain")
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        // create file in download directory.
        put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS)
}.let { values ->
    context.contentResolver.insert(MediaStore.Files.getContentUri("external"), values)
if (fileUri == null) return
context.contentResolver.openOutputStream(fileUri)?.use { fileOut ->
    fileOut.write("Hello, World!".toByteArray())
val file = File(fileUri.path!!) // File("/external/file/9359")

我可以保证代码是正确的,因为我可以在 Download 文件夹中看到正确的文件。

我的FileProvider设置

我已在AndroidManifest.xml注册供应商:

<application>
    <provider
        android:name="androidx.core.content.FileProvider"
        android:authorities="my.provider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/filepath" />
    </provider>
</application>

在下面使用filepath.xml:

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <external-path
        name="external"
        path="." />
</paths>

我也尝试过这么多路径变体,一个接一个:

<external-files-path name="download" path="Download/" />
<external-files-path name="download" path="." />
<external-path name="download" path="Download/" />
<external-path name="download" path="." />
<external-path name="external" path="." />
<external-files-path name="external_files" path="." />

我甚至注册了外部存储读取权限:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

我哪部分做错了?

编辑1

在发帖之后,我认为 File 初始化可能是问题所在:

File(fileUri.path!!)

所以我把它改成了

File(fileUri.toString())

但还是不起作用。

它们的内容差异如下:

fileUri.path       -> /external/file/9359
(file.path)        -> /external/file/9359
error              -> /external/file/9359
fileUri.toString() -> content://media/external/file/9359
(file.path)        -> content:/media/external/file/9359
error              -> /content:/media/external/file/9359

编辑2

我最初想要实现的是 向其他应用程序发送二进制数据 。据官方记载,它似乎只接受文件 Uri 。

如果有其他方法来实现这一点,比如直接共享 File 等等,我将不胜感激。

但是我现在想知道的是简单的--我如何使 FileProvider 可用以获取/读取/生成文件 Uri (如 /external/file/9359 等)?可能会带来帮助,不仅仅是这个情况,对我来说似乎是一个更一般的/基本的知识。

7 1.5K 0 票数 3
EN
android
uri
mediastore
android-fileprovider

回答 7

Stack Overflow用户

回答已采纳

发布于 2022-02-22 15:12:24

内容uris没有文件路径或方案。您应该创建一个输入流,并通过使用这个输入流创建一个临时文件。您可以从此文件提取文件-uri或路径。下面是我的一个函数,用于从content创建一个临时文件:

 fun createFileFromContentUri(fileUri : Uri) : File{
    var fileName : String = ""
    fileUri.let { returnUri ->
        requireActivity().contentResolver.query(returnUri,null,null,null)
    }?.use { cursor ->
        val nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
        cursor.moveToFirst()
        fileName = cursor.getString(nameIndex)
    //  For extract file mimeType
    val fileType: String? = fileUri.let { returnUri ->
        requireActivity().contentResolver.getType(returnUri)
    val iStream : InputStream = requireActivity().contentResolver.openInputStream(fileUri)!!
    val outputDir : File = context?.cacheDir!!
    val outputFile : File = File(outputDir,fileName)
    copyStreamToFile(iStream, outputFile)
    iStream.close()
    return  outputFile
fun copyStreamToFile(inputStream: InputStream, outputFile: File) {
    inputStream.use { input ->
        val outputStream = FileOutputStream(outputFile)
        outputStream.use { output ->
            val buffer = ByteArray(4 * 1024) // buffer size
            while (true) {
                val byteCount = input.read(buffer)
                if (byteCount < 0) break
                output.write(buffer, 0, byteCount)
            output.flush()
   }

--编辑--

内容Uri有路径,但没有文件路径。例如;

内容Uri路径:内容://media/外部/音频/media/710,

文件uri路径: file:///sdcard/media/audio/ringtones/nevergonnagiveyouup.mp3

通过使用上面的函数,您可以创建一个临时文件,并且可以提取uri和路径,如下所示:

val tempFile: File = createFileFromContentUri(contentUri)  
val filePath = tempFile.path  
val fileUri = tempFile.getUri() 

在使用tempFile之后,删除它以防止内存问题(将被删除,因为它是在缓存中编写的):

tempFile.delete()

不需要编辑内容uri。将方案添加到内容uri可能不起作用

票数 2
EN

Stack Overflow用户

发布于 2022-07-04 09:14:18

首先,在最近的Android中,您通常不会从Uri中获取文件路径,因为您可能只有临时访问权限,所以通常使用 context.contentResolver.openInputStream(externalUri) 将其流中的内容复制到随后可以使用的文件中。

通过这样做,您将一个文件放在一个不属于您的应用程序的目录中,因此 FileProvider可能不需要 ,您只需传递从MediaStore插入获得的uri。

使用FileProvider的正确方法是将文件放入您的FileProvider路径之一,而不使用MediaStore,因此假设您有

<provider
        android:name="androidx.core.content.FileProvider"
        android:authorities="${applicationId}.provider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/filepath" />
</provider>

和

<external-files-path name="external" path="shared" />

您将在该路径中创建文件。

val sharedFolder=context.getExternalFilesDir("shared")
val fileName="test.txt"
File(sharedFolder,fileName).outputStream()?.use { fileOut ->
    fileOut.write("Hello, World!".toByteArray())
val fileProviderUri = FileProvider.getUriForFile(
                    context,
                    BuildConfig.APPLICATION_ID + ".provider",
                    File(sharedFolder,fileName)
//example intent for viewing file, to change if you need to pass to another application
                context.startActivity(
                    Intent(Intent.ACTION_VIEW).setData(fileProviderUri )
                        .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
                )
票数 1
EN

Stack Overflow用户

发布于 2022-02-15 05:52:24

兄弟,试试这个,也许它对你有用,把你的path.xml文件改成这个;

<?xml version="1.0" encoding="utf-8"?>
<paths>
  <external-path
    name="external"
    path="." />
  <external-files-path
    name="external_files"
    path="." />
  <cache-path
    name="cache"
    path="." />
  <external-cache-path
 
推荐文章
神勇威武的泡面  ·  Web API 实现 - Best practices for cloud applications | Microsoft Learn
2 月前
玩篮球的西装  ·  HttpClientJsonExtensions.PostAsJsonAsync 方法 (System.Net.Http.Json) | Microsoft Learn
1 月前
知识渊博的豆芽  ·  nginx正向代理-内网服务器通过代理服务器访问外部网络_nginx以允许内部系统通过http代理服务器访问外部网络-CSDN博客
11 月前
另类的滑板  ·  C# 怎样知道List<string>里 每个元素重复了多少次?_百度知道
1 年前
严肃的橡皮擦  ·  [后端]gitlab之gitlab-ci自动部署 - 简书
1 年前
气宇轩昂的葫芦  ·  Group By in Java 8 - Javatpoint
1 年前
旅行中的凉茶  ·  oracle pl sql split string to array-掘金
2 年前
今天看啥   ·   Py中国   ·   codingpro   ·   小百科   ·   link之家   ·   卧龙AI搜索
删除内容请联系邮箱 2879853325@qq.com
Code - 代码工具平台
© 2024 ~ 沪ICP备11025650号