I wanna get(read/generate) file Uri -- with path like below -- by FileProvider, but don't know how:
val file = File("/external/file/9359") // how do I get its Uri then?
All I know was FileProvider.getUriForFile method, but it throws exception.
I was trying to mock a download progress -- create a file in Download folder, and then pass it to ShareSheet to let user do whatever it want to do.
What I have done:
Download by using MediaStore and ContentResolver.ShareSheet util function.FileProvider and filepath.xml.In my case, I want to share the file via ShareSheet function, which requires
Filebut the usual file.toUri() will throw exception above SDK 29. Hence I change it into FileProvider.getUriForFile() as Google-official recommended.
val fileUri: Uri = FileProvider.getUriForFile(context, "my.provider", file)
Will throw exception:
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")
I can assure the code is correct because I can see the correct file in Download folder.
I have registered provider in 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>
with filepath.xml below:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path
name="external"
path="." />
</paths>
I've also tried so many path variants, one by one:
<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="." />
I also even registered external storage reading permissions:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
which part did I do wrong?
After posting, I thought the File initialization might be the problem:
File(fileUri.path!!)
So I changed it to
File(fileUri.toString())
but still not working.
Their content difference is below btw:
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
What I originally wanted to achieve is sending binary data to other app. As Official-documented, it seems only accept nothing but file Uri.
I'd be appreciate if there's any other way to achieve this, like share the File directly, etc.
But what I'm wondering now is simple -- How do I make FileProvider available to get/read/generate file Uri like /external/file/9359 or so on? This might comes in help not only this case, and seems like a more general/basic knowledge to me.
Content uris does not have file-path or scheme. You should create an input stream and by using this inputstream create a temporary file. You can extract a file-uri or path from this file. Here is a function of mine to create a temporary file from content-uri:
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()
}
}
}
--Edit--
Content Uri has path but not file path. For example;
content Uri path: content://media/external/audio/media/710 ,
file uri path : file:///sdcard/media/audio/ringtones/nevergonnagiveyouup.mp3
By using the function above you can create a temporary file and you can extract uri and path like this:
val tempFile: File = createFileFromContentUri(contentUri)
val filePath = tempFile.path
val fileUri = tempFile.getUri()
After using tempFile delete it to prevent memory issues(it will be deleted because it is written in cache ):
tempFile.delete()
There is no need to edit content uri. Adding scheme to content uri probably not gonna work
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With