2

I have local files in my app which I read like this:

<Image source={require("../../img/pic1.jpg") />

How to read this file using RNFS.readFile(filepath, "base64"); using RNFS.readFile("../../img/pic1.jpg", "base64"); gives me error "no such file"

rendom
  • 3,417
  • 6
  • 38
  • 49

4 Answers4

18

react-native-fs will only access the files from device storage. So this problem poses a quandary. These files are bundled at build time so how can we access them in development vs production?

My project structure

├── android
├── app
│   └── assets
│       └── images
└── ios

I store my images inside ./app/assets/images/

Development

When in Development or debug the files are served to the device via the metro bundler so they are not actually stored on the device. So accessing them from the devices storage would not be possible. You would have to provide some sort of mocked functionality for when you are developing. It is possible to use __DEV__ to allow different functions to run depending on the environment https://facebook.github.io/react-native/docs/javascript-environment

if (__DEV__) {
  // do something in development
} else {
  // do something in production
}

Production

From some research it does seem like it is possible to access the files in production.

iOS

What I have discovered is that the files reside in a folder called assets that are in the MainBundlePath.

So if you want to access the following file in my local directory ./app/assets/images/image.png you would need the following file path: RNFS.MainBundlePath + /assets/app/assets/images/image.png to access it on the device.

You can check to see the file path by looking at the Packaging.log that is generated with your ipa.

Android

Android doesn't have a folder called MainBundlePath that is only on iOS. I have spent some time researching and looking at the different folders on the device but I was unable to access the images that are bundled. It would seem that they not accessible in the way that we would hope using react-native-fs.

Possible solutions

rn-fetch-blob

rn-fetch-blob has functionality for both ios and Android to read from the assets directory

To access the files for iOS you would do something like this:

RNFetchBlob.fs.stat(fs.dirs.MainBundleDir + '/whatever/files/you/added.mp3').then( stats => {...});

And for Android you would do something like this:

RNFetchBlob.fs.stat(fs.asset('/whatever/files/you/added.mp3').then( stats => {...});

It does give this warning about the assets being compressed by Gradle.

Gradle will compress bundled assets by default. In order to interact with asset files with this module, compression must be disabled. Please see How to access files from assets for more detail.

This could explain why I was unable to find any assets in Android when looking using react-native-fs

You can read more about using rn-fetch-blob and its filesystem here https://github.com/joltup/rn-fetch-blob#user-content-file-system

Storing the files in the native projects

Rather than relying on the bundler to package up the images for you and put them on the device. You could store copies of them already in the iOS and Android projects. This has the bonus that the images would be available in development, however you would storing the images multiple times and it could lead to larger application sizes.

Storing the images as base64

You could store the images as base64 strings in json files and then require these by your application. That would then mean that you would have them already in base64 (so the app doesn't have to do any conversion) but again you would be increasing the size of your application. If it is only a small number of images it may be the easiest option.

Andrew
  • 26,706
  • 9
  • 85
  • 101
3

2021 update for using RNFS

iOS

Andrew's suggestion for using RNFS.MainBundlePath still works fine.

Android

RNFS can read files in the app asset directory with:

RNFS.readFileAssets(filepath:string, encoding?: string)

Encoding can be one of utf8 (default), ascii, or base64. Use base64 for reading binary files. filepath is the relative path to the file from the root of the assets folder.

Example if your file is directly in your assets directory:

RNFS.readFileAssets('coolimage.png', 'base64');

https://github.com/itinance/react-native-fs#readfileassetsfilepathstring-encoding-string-promisestring

-1

Just using filename = response.uri.replace('file:', '')

The final code:

if (Platform.OS === 'ios') {
      filename = response.uri.replace('file:', '')
} else {
      filename = response.uri
}

Working now ...

Azhar
  • 20,500
  • 38
  • 146
  • 211
  • Hi I am trying to do the same but keep getting the same error. Any chance you can share your code? I am not sure where or how you getting the response.uri? thanks in advance – dogwasstar Oct 20 '19 at 08:45
-1

Converting to Base64 Works

 const DownloadUrl = async () => {
var RNFS = require("react-native-fs");

RNFS.downloadFile({
  fromUrl: "https://facebook.github.io/react-native/img/header_logo.png",
  toFile: `${RNFS.DocumentDirectoryPath}/react-native.png`,
}).promise.then(async (response: string) => {
  RNFS.readFile(
    `${RNFS.DocumentDirectoryPath}/react-native.png`,
    "base64"
  ).then((base64String: any) => {
    console.log(
      "base64String===>",
      `${RNFS.DocumentDirectoryPath}/react-native.png`,
      base64String
    );
  });
  console.log("response ====>", RNFS.DocumentDirectoryPath, response);
});

};

Vishesh Gupta
  • 165
  • 1
  • 5