3

I am using Storage Access Framework(SAF) in my app. In order to scan a file(photo or document) i need send a local file path trough an API. I've managed to setup the SAF correctly, and now when a user chooses a file and i am getting a Uri as it supposed to be.

As it seems the Uri is a key for the cloud and not a local file.

The Uri value looks like that:

content://com.android.providers.media.documents/document/image:11862 

How can i convert this Uri into a local file? Should i download the file from the cloud? How can i do it?

Montoya
  • 2,819
  • 3
  • 37
  • 65

2 Answers2

5

As it seems the Uri is a key for the cloud and not a local file.

The Uri is an opaque reference to a piece of content. You have no way of knowing where the data is, nor should you care.

How can i convert this Uri into a local file?

Ideally, you don't. Ideally, you "scan a file(photo or document)" using some library that supports an InputStream. In that case, you use ContentResolver and openInputStream() to get the stream to pass to the library.

If your library does not support InputStream as a data source, you will need to use openInputStream() yourself, using Java I/O to make a copy of the content as a file in the filesystem, for you to pass to the library.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • What about when a library only accepts a File or an absolute path to a directory? – masterwok Jul 23 '18 at 14:23
  • 1
    @masterwok: I'd start by pointing the library authors to [this five-year-old blog post of mine](https://commonsware.com/blog/2013/08/07/for-android-apis-think-streams-not-files.html) asking library authors to think in terms of streams, not files. And, until they improve their API, you would need to use the `InputStream` from my answer to copy the content to a file that you control (e.g., in `getCacheDir()`). Then, pass the path to that file to the library. – CommonsWare Jul 23 '18 at 21:29
  • Thank you for the quick reply. However, I'm not sure I understand how this would work for directories. The library I'm using has a parameter for download location. It then downloads one or more files to the provided location. If the developer of the library were to accept a Uri with a content scheme for some parent location, then could they create multiple "files" at that location using ContentResolver and InputStream somehow? – masterwok Jul 23 '18 at 21:48
  • 1
    @masterwok: If the library is Android-specific, and you got the `Uri` via `ACTION_OPEN_DOCUMENT_TREE`, they could in theory create whatever they need inside of there. The "tree" `Uri` is the Storage Access Framework equivalent of a directory, so the library could use `DocumentFile` and `ContentResolver` to create documents inside of that tree. Or, have the library download the stuff to a directory that you control, then you worry about transferring the content to the document tree. Or, skip the Storage Access Framework and stick with the filesystem. – CommonsWare Jul 23 '18 at 22:14
  • Awesome, I think I see how it comes together now. Thank you! – masterwok Jul 23 '18 at 22:25
0

You can read directly from the underlying file, with random access, under the following conditions:

  • The URI points to a local file, you can check this comparing the URI authority with the value com.android.externalstorage.documents;
  • You have both read and write access to the URI.
ParcelFileDescriptor parcelFileDescriptor = getContext().getContentResolver().openFileDescriptor(documentUri, "rw");
FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
FileChannel channel = new FileInputStream(fileDescriptor).getChannel();

Store the FileDescriptor somewhere as long as you keep the FileChannel open: if that object gets garbage collected the channel will be no longer able to access the file.

gxcare
  • 511
  • 4
  • 12