Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Content URI support #395

Merged
merged 1 commit into from
Dec 10, 2017
Merged

Content URI support #395

merged 1 commit into from
Dec 10, 2017

Conversation

krzysztof-miemiec
Copy link

This PR is based on #49, I used the same way of getting InputStreams/OutputStreams as @jrichardlai. It should fix #351.

New methods

  • getFileUri() that returns Uri for any passed path (with or without file:// prefix or with content:// prefix)
  • getInputStream() that returns InputStream for any Uri passed
  • getOutputStream() that returns OutputStream for any Uri passed
  • getInputStreamBytes() that reads the whole content of an InputStream and returns it as a byte array

Modified methods

Added content uri support to:

  • readFile()
  • writeFile()
  • appendFile()
  • write() - Note: random access (position>=0) is not supported for content uris
  • read()
  • copyFile() - both source and destination can be content uris
  • copyInputStream() - output stream can be a content uri

Others

  • IORejectionException - an exception that accepts a code besides a message

# New methods
* `getFileUri()` that returns Uri for any passed path (with or without file:// prefix or with content:// prefix)
* `getInputStream()` that returns InputStream for any Uri passed
* `getOutputStream()` that returns OutputStream for any Uri passed
* `getInputStreamBytes()` that reads the whole content of an InputStream and returns it as a byte array
# Modified methods
Added content uri support to:
* `readFile()`
* `writeFile()`
* `appendFile()`
* `write()` - Note: random access (position>=0) is not supported for content uris
* `read()`
* `copyFile()` - both source and destination can be content uris
* `copyInputStream()` - output stream can be a content uri
# Others
* IORejectionException - an exception that accepts a code besides a message
@lukasbowen
Copy link

Looks good, but can I ask why the custom exception class?

@krzysztof-miemiec
Copy link
Author

I added it to preserve an error code (ENOENT/EISDIR) in rejected promise, because I moved error handling to other methods that do not take Promise as an input. I'd be glad to hear if there's a better way to do it.

@lukasbowen
Copy link

lukasbowen commented Nov 29, 2017

I actually like what you did a lot. I think it cleans ups and makes everything a bit more elegant. I was just curious about the thought behind making the change.

woffleloffle added a commit to woffleloffle/react-native-fs that referenced this pull request Nov 30, 2017
@itinance itinance merged commit 42aeaf8 into itinance:master Dec 10, 2017
@itinance
Copy link
Owner

thank you very much!

@ankag94
Copy link

ankag94 commented Jan 3, 2018

I am still getting an error of "Permission Denial" for the content uri with copyFile().
Actual URI : content://com.android.providers.media.documents/document/image:26736

the stacktrace for the error is like:

W/System.err: java.lang.SecurityException: Permission Denial: reading com.android.providers.media.MediaDocumentsProvider uri content://com.android.providers.media.documents/document/image:26823 from pid=5731, uid=10226 requires that you obtain access using ACTION_OPEN_DOCUMENT or related APIs W/System.err: at android.os.Parcel.readException(Parcel.java:2004) W/System.err: at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:183) W/System.err: at android.database.DatabaseUtils.readExceptionWithFileNotFoundExceptionFromParcel(DatabaseUtils.java:146) W/System.err: at android.content.ContentProviderProxy.openTypedAssetFile(ContentProviderNative.java:698) W/System.err: at android.content.ContentResolver.openTypedAssetFileDescriptor(ContentResolver.java:1410) W/System.err: at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:1247) W/System.err: at android.content.ContentResolver.openInputStream(ContentResolver.java:967) W/System.err: at com.rnfs.RNFSManager.getInputStream(RNFSManager.java:82) W/System.err: at com.rnfs.RNFSManager.copyFile(RNFSManager.java:330) W/System.err: at com.rnfs.RNFSManager.copyFile(RNFSManager.java:320) W/System.err: at java.lang.reflect.Method.invoke(Native Method) W/System.err: at com.facebook.react.bridge.JavaMethodWrapper.invoke(JavaMethodWrapper.java:374) W/System.err: at com.facebook.react.bridge.JavaModuleWrapper.invoke(JavaModuleWrapper.java:162) W/System.err: at com.facebook.react.bridge.queue.NativeRunnable.run(Native Method) W/System.err: at android.os.Handler.handleCallback(Handler.java:790) W/System.err: at android.os.Handler.dispatchMessage(Handler.java:99) W/System.err: at com.facebook.react.bridge.queue.MessageQueueThreadHandler.dispatchMessage(MessageQueueThreadHandler.java:31) W/System.err: at android.os.Looper.loop(Looper.java:164) W/System.err: at com.facebook.react.bridge.queue.MessageQueueThreadImpl$3.run(MessageQueueThreadImpl.java:194) W/System.err: at java.lang.Thread.run(Thread.java:764)

and the exception thrown appears to be:
Exception caught while picking file: Error: Permission Denial: reading com.android.providers.media.MediaDocumentsProvider uri content://com.android.providers.media.documents/document/image:26823 from pid=5731, uid=10226 requires that you obtain access using ACTION_OPEN_DOCUMENT or related APIs

@krzysztof-miemiec
Copy link
Author

@ankag94 I have tested this PR on several Android versions (API 23, 25). An exception you're getting may be caused by calling getContentResolver() from reactContext instead of the context of activity that has requested the file or trying to open an Uri that has not been requested by the app. How do you obtain your uri/path?

@mtolly
Copy link

mtolly commented Jan 3, 2018

This is working great for us to copy image URLs returned from the camera roll. Thank you so much!

@annammarri
Copy link

annammarri commented Apr 6, 2018

I'm trying to use this method getInputStream() but I have this error: '_reactNativeFs2.default.getInputStream is not a function' and my code looks like this: RNFS.getInputStream(uri).then({...}).catch({...})
what could it be?

@krzysztof-miemiec
Copy link
Author

@annammarri getInputStream() is available only internally in native Android code. You can use one of the methods annotated with @ReactMethod. And yes - it means that you can't read the file piece by piece, as if you would do it using streams 😞

@smithaitufe
Copy link

Please is it possible to get the actual path using this package?

@imran1992
Copy link

I want to read text from text file placed in the 'files' folder of the app directory Android.

const path =RNFS.DocumentDirectoryPath+ "/rn.txt";
const content = await RNFS.readFile(path, "utf8");
return content;

Not Working!!!

@krzysztof-miemiec
Copy link
Author

@imran1992, please see what console.log(RNFS.DocumentDirectoryPath) prints, I think you meant to use RNFS.ExternalDirectoryPath or RNFS.ExternalStorageDirectoryPath.

@imran1992
Copy link

imran1992 commented Jun 27, 2018

Thanks @krzysztof-miemiec
RNFS.ExternalDirectoryPath and RNFS.ExternalStorageDirectoryPath. Both are working accordingly.

readFile = async (MyPath) => {
    try {
      const path =MyPath+ "/rn.txt";
      const contents = await RNFS.readFile(path, "utf8");
      return("" + contents);
    } catch (e) {
      alert("" + e);
    }
  };

 <Button title="AppFilesDir" onPress={() => this.readFile(RNFS.ExternalDirectoryPath)} />
 <Button title="InternalStorageDir" onPress={() => this.readFile(RNFS.ExternalStorageDirectoryPath)} />

ITS Working .....
But Now how to grab text file by following directory:

content://com.android.externalstorage.documents/document/primary%3AAndroid%2Fdata%2Fcom.smsappt%2Ffiles%2Frn.txt

I received it from react-native-document-picker package

@Purvik
Copy link

Purvik commented Jul 19, 2019

@imran1992 Hey, I'm using react-native-document-picker to pick multiple files from device. On iOs all works fine with returned URI but on Android returned content URI keeps failing my calling file upload API.
I know it's related to convert content URI to file path but still I failed to get any exact solution.

@catmans1
Copy link

catmans1 commented Sep 13, 2019

I'm using this lib, and want to get content:// provider, but it occur error "Folder does not exist"
although i write Android native, it work well,
Could suggest any solutions for that?
My code:
const uriContent = 'content://com.example.myapplication.StudentsProvider/students' RNFS.readDir(uriContent) .then((result) => { console.log('GOT RESULT', result); // stat the first file return Promise.all([RNFS.stat(result[0].path), result[0].path]); }) .then((statResult) => { if (statResult[0].isFile()) { // if we have a file, read it return RNFS.readFile(statResult[1], 'utf8'); } // return 'no file'; alert(JSON.stringify('no file')) }) .then((contents) => { // log the file contents console.log(contents); alert(JSON.stringify(contents)) }) .catch((err) => { console.log(err.message, err.code); alert(JSON.stringify(err.message)) });

@enigmablue
Copy link

enigmablue commented Apr 10, 2020

Hi, wondering if there is any solution for this? I'm also stuck where my simple content:// is getting file not found on a RNFS.stat Example of my content URI:

content://com.android.providers.downloads.documents/document/590

This is coming from react-native-document-picker

@marcodafonseca
Copy link

Hi

I'm also having problems with this. How does one convert the content uri to the proper full path?

Here is an example of the kind of path I'm dealing with: content://com.android.providers.media.documents/document/image%3A19030

@GerdaAm
Copy link

GerdaAm commented May 5, 2020

with rn fetch blob i read the file and then write to directory, however, for large pdf files this fails. So I am trying to figure out how to create a blob from this content uri?

@Rajathjagatap
Copy link

Did any one find solution for converting uri to actual path?

@codetheweb
Copy link

codetheweb commented Jun 3, 2020

content://com.android.providers.downloads.documents/document/590

This is coming from react-native-document-picker

I ended up having to use RNFS.copyFile to copy the file by URI to a temp file, then RNFS was able to read the file correctly. For example:

import RNFS from 'react-native-fs';
...
const destPath = `${RNFS.TemporaryDirectoryPath}/${shortid.generate()}`;
await RNFS.copyFile(selectedDocument.uri, destPath);

console.log(await RNFS.stat(destPath);

@MaiconGilton
Copy link

this may help you https://stackoverflow.com/a/62720645/9894200

@muhammad-ihsan
Copy link

RNFS

hello @codetheweb , where RNFS is coming from? or would you help to provide complete code?

@codetheweb
Copy link

@muhammad-ihsan updated the code example.

@Nikoms
Copy link

Nikoms commented Aug 21, 2020

@codetheweb But I guess that you lose the original "creation date" :(

@noobmaster6981
Copy link

I'm trying to use the getFileUri() method mentioned here but have no idea how to use it.
What I'm trying to achieve is I have a content uri which I'm getting from DocumentPicker which I want to convert into a file uri
Can someone please help me out?
I'm new to react native

This PR is based on #49, I used the same way of getting InputStreams/OutputStreams as @jrichardlai. It should fix #351.

New methods

  • getFileUri() that returns Uri for any passed path (with or without file:// prefix or with content:// prefix)
  • getInputStream() that returns InputStream for any Uri passed
  • getOutputStream() that returns OutputStream for any Uri passed
  • getInputStreamBytes() that reads the whole content of an InputStream and returns it as a byte array

Modified methods

Added content uri support to:

  • readFile()
  • writeFile()
  • appendFile()
  • write() - Note: random access (position>=0) is not supported for content uris
  • read()
  • copyFile() - both source and destination can be content uris
  • copyInputStream() - output stream can be a content uri

Others

  • IORejectionException - an exception that accepts a code besides a message

@sourav-singh-rawat
Copy link

import RNFS from 'react-native-fs';

`const contentResolvedData = await RNFS.stat(uri);
const file_name = contentResolvedData.originalFilepath.split('/').pop();

let path = ${RNFS.TemporaryDirectoryPath}/${file_name};
await RNFS.copyFile(data, path);
path = file://${path};

const ext = file_name?.split('.').pop();
const file_type = mimeType.split('/')[0];`

@enigmablue
Copy link

enigmablue commented Jun 3, 2021

import RNFS from 'react-native-fs';

`const contentResolvedData = await RNFS.stat(uri);
const file_name = contentResolvedData.originalFilepath.split('/').pop();

let path = ${RNFS.TemporaryDirectoryPath}/${file_name};
await RNFS.copyFile(data, path);
path = file://${path};

const ext = file_name?.split('.').pop();
const file_type = mimeType.split('/')[0];`

Unable to get this to work because of permission error. It works locally, even on release but when i put it on APK it dosen't work. Am on API 29

'Error: ENOENT: /storage/emulated/0/Download/xxxx.pdf: open failed: EACCES (Permission denied), open '/storage/emulated/0/Download/xxx.pdf''

I think RNFS don't work with API 29 for files in the download folder anymore? I checked that the read/write external storage is granted, and have the requestLegacyExternalStorage="true" on as well (which i suspect is not working on APK/Playstore)

Correspondingly, the RNFS.upload dosen't work also because its getting socket closed (not able to pick up the file)

@marcodafonseca
Copy link

I am able to use save/download to the download folder.

Extract from gradle:

ext {
    buildToolsVersion = "29.0.2"
    minSdkVersion = 21
    compileSdkVersion = 29
    targetSdkVersion = 29
    supportLibVersion = "29.0.0"
}

Download function:

import RNFS from "react-native-fs";

let options: RNFS.DownloadFileOptions = {
    fromUrl: externalUrl,
    toFile: `${RNFS.DownloadDirectoryPath}/${fileName}`,
    connectionTimeout: 60000,
    cacheable: false,
};

var response = await RNFS.downloadFile(options).promise;

Permissions requested:

PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE,
PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE,

Manifest permissions:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />

I hope this helps?

@enigmablue
Copy link

Thanks for reverting so quickly @marcodafonseca
I have the same settings actually. but i'm doing upload, and readFile / copyFile . Still dosen't work.
I'd play around a bit, and maybe try it on a completely new repo

@marcodafonseca
Copy link

No worries, @enigmablue

Let me know how your new repo test goes.
If your new repo test fails feel free to upload it to Github and invite me. I'll happily take a quick look and see if I can spot the issue if you like

@enigmablue
Copy link

enigmablue commented Jun 3, 2021

Unfortunately still dosen't work :( @marcodafonseca
I replicated it in the repo here. The two permissions are enabled. RNFS.stat works on the file. But readfile fails, which is essential for the uploadfile.

https://github.com/EnigmaGlobe/rnfspdftest/tree/master

(ps: if i'm not wrong, setting requestLegacyExternalStorage works on testing but fails on APK/playstore)

@enigmablue
Copy link

enigmablue commented Jun 8, 2021

Found the solution, just needed base64 encoding to read the pdf file. And copyfile wouldn't work in that context because it didn't have encoding in the input.

  let exportedFileContent = await RNFS.readFile(path, "base64");
      // set your DocumentDirectoryPath pth
      let path = RNFS.DocumentDirectoryPath + "/cache.pdf";

      // write the file
        RNFS.writeFile(path, exportedFileContent, 'base64')
          .then((success) => {
            console.log('FILE WRITTEN! ',success);
          })

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

access content://com.android.providers.media.documents/document/image%3A99