URI not found
Closed this issue · 9 comments
Describe the bug
When trying to upload a task using the UploadTask.fromAndroidUri
constructor, the task always throws a URI not found: [uri]
error.
To Reproduce
Steps to reproduce the behavior:
- Create an upload task using
UploadTask.fromAndroidUri
- Enqueue the task to the downloader
- The error gets thrown once the task gets started
The issue happens in the pathFromUri
method:
On line 278 the method tries to retrieve the file path using the created content resolver cursor. Using the debugger, I found out that that line returns
null
for every file I try to upload.
Here is the code that creates the file picker to access the URI:
channel.setMethodCallHandler { call, result ->
this.pendingResult = result
if (call.method == "showPicker") {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
val uri = Uri.parse(Environment.getExternalStorageDirectory().path + File.separator)
intent.setDataAndType(uri, "*/*")
intent.setType("*/*")
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
intent.putExtra("multi-pick", true)
startActivityForResult(Intent.createChooser(intent, "Select a File to Upload"),1)
}
}
I don't know enough about SAF and MediaStore to understand what the correct process of retrieving a file from a URI would be, but after looking around for quite some time I've come to the conclusion that it's inconclusive and almost every solution differs from the others and is highly dependant on the SDK version... In this case I'm not even sure if it's the package's fault or my fault for calling the system file picker incorrectly.
Can you share the actual URI you are feeding into the UploadTask.fromUri
call (which presumably is generated by the FilePicker code you implemented natively)? Can you also explain what file you are picking using the file picker? For example, is this a file stored in the Documents directory, or one in the Photos external memory directory, etc?
The background_downloader implementation expects the content URI to be stored in the MediaStore - you're getting null because apparently it is not. That's why I would like to see the actual URI
Here is the URI: content://com.android.providers.media.documents/document/image%3A1000051742
It was picked from the camera folder on my device.
I also tried picking a photo from Google Photos. This time the error went like this:
I/TaskWorker(16946): Using filepath /storage/emulated/0/DCIM/Camera/PXL_20240427_215616814.MP.jpg from URI content://com.google.android.apps.photos.contentprovider/0/1/content%3A%2F%2Fmedia%2Fexternal%2Fimages%2Fmedia%2F1000041931/ORIGINAL/NONE/image%2Fjpeg/99241118
D/TaskWorker(16946): Binary upload for taskId d58a374b-fc32-4695-a0cf-175d24660e4f
W/TaskWorker(16946): Error for taskId d58a374b-fc32-4695-a0cf-175d24660e4f: /storage/emulated/0/DCIM/Camera/PXL_20240427_215616814.MP.jpg: open failed: EACCES (Permission denied)
W/TaskWorker(16946): java.io.FileNotFoundException: /storage/emulated/0/DCIM/Camera/PXL_20240427_215616814.MP.jpg: open failed: EACCES (Permission denied)
So it seems like that time the URI got successfully resolved to a file path but even then the app didn't have permissions to access it.
My main goal is to provide the ability to pick any file from the device (including from storage providers such as Google Photos) and upload them using the URI, without creating a copy of the file (the cloning of files takes up a lot of cache and has been a major pain point for our clients). From what I understand, this is impossible using MediaStore, so I have to use the SAF actions and URIs.
From Android 10 onward there are restrictions on accessing files generated by other apps (i.e. not the app you are writing) that prevent us from getting a direct file path. The only access you can get is to a stream of data, provided you have the appropriate permissions, and that is not currently how the upload logic is implemented (it expects a file path). I may look into this later, but it is complex and affects a lot of the code for the uploader, so not sure when I can get to that.
Either way, also for the Google Photos example, you do need proper user permission to access the files you let the user pick. I don't believe the file picker automatically grants permissions for the file you pick. You have to look into that - that goes beyond the scope of the downloader package.
Ok, it looks like this blog post cleared things up quite a bit. I'm guessing the fromAndroidUri
constructor falls under the "Pretend That the MediaStore Knows What You’re Talking About" part.
So currently the only options would be to:
- implement InputStream support (which will be very difficult)
- create a copy of the file and get a cache path (which is what file_picker does)
- use MediaStore to request the URI (which doesn't provide non-media files)
Yes it's a bit of a mess. I can convert to use an input stream, but then I am concerned I may not have the file size ahead of time, therefore cannot set the content-length header and the upload may fail. As mentioned, I'll get to this but it will take some time.
Theoretically the file size can be retrieved the same way as the filename, by reading the OpenableColumns.SIZE value. Of course that has the same issue as the filename - it might not be defined.
I think in that case you could just attempt the upload anyway and if that fails then it's up to the developer to deal with a storage provider that doesn't provide the file size.
This issue is stale because it has been open for 14 days with no activity.
This issue was closed because it has been inactive for 7 days since being marked as stale.
Published in V8.9.0