热心的移动电源 · Java判断操作系统是32位还是64位_德莱 ...· 1 年前 · |
安静的毛豆 · pytorch每次分类结果都不同,有细微差异 ...· 1 年前 · |
严肃的打火机 · 【Vue3.x】全局组件、局部组件和递归组件 ...· 1 年前 · |
腹黑的火柴 · JavaScript toJSON() ...· 1 年前 · |
TL;DR
我想获得(读取/生成)文件
Uri
--路径类似于下面的路径--
FileProvider
,但不知道如何获得:
val file = File("/external/file/9359") // how do I get its Uri then?
我只知道
FileProvider.getUriForFile
方法,但是它会抛出异常。
我在做什么
我试图模拟下载过程--在
Download
文件夹中创建一个文件,然后将它传递给
ShareSheet
,让用户做它想做的任何事情。
我所做的:
Download
中使用
MediaStore
和
ContentResolver
创建文件。
ShareSheet
util函数。
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
等)?可能会带来帮助,不仅仅是这个情况,对我来说似乎是一个更一般的/基本的知识。
发布于 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可能不起作用
发布于 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)
)
发布于 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