Search code examples
androidreact-nativeandroid-10.0react-native-fs

Write files to the Android external storage using React Native FS


My React Native Android application needs to save a JSON file in the special folder located in the external storage. I am trying to do it using RNFS (https://github.com/itinance/react-native-fs) this way:

const saveData = async () => {
    var path = `${RNFS.ExternalStorageDirectoryPath}/MyApp`;
    RNFS.mkdir(path);
    path += '/data.json';
    RNFS.writeFile(path, JSON.stringify(getData()), 'utf8')
      .then((success) => {
        console.log('Success');
      })
      .catch((err) => {
        console.log(err.message);
      });
  }

It works well but fails on Android Q device. This error is shown:

Error: Directory could not be created

If I try to write a plain file without creating directory it throws this error:

ENOENT: open failed: ENOENT (No such file or directory), open '/storage/emulated/0/data.json'

However, I've added this permissions to my AndroidManifest.xml:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

And granted the external storage permissions in the settings. But if I change the RNFS.ExternalStorageDirectoryPath to RNFS.DocumentDirectoryPath it works without any errors. But I need to get an access to the external storage. Is there any way to do it?


Solution

  • I've found out that legacy external storage access is required from Android API 29+. So, I've editied my AndroidManifest.xml (that located in android/app/src/main/) like this:

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.appName">
    
        <uses-permission android:name="android.permission.INTERNET" />
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
        <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    
        <application
        ...
          android:requestLegacyExternalStorage="true"
        ...
        >
        </application>
    </manifest>
    

    And everything has started to work. Also, I've added a request for granting permissions to the saveData function:

    const saveData = async () => {
        try {
          const granted = await PermissionsAndroid.requestMultiple([
            PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE,
            PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE,
          ]);
        } catch (err) {
          console.warn(err);
        }
        const readGranted = await PermissionsAndroid.check(PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE); 
        const writeGranted = await PermissionsAndroid.check(PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE);
        if(!readGranted || !writeGranted) {
          console.log('Read and write permissions have not been granted');
          return;
        }
        var path = `${RNFS.ExternalStorageDirectoryPath}/MyApp`;
        RNFS.mkdir(path);
        path += '/data.json';
        RNFS.writeFile(path, JSON.stringify(getData()), 'utf8')
          .then((success) => {
            console.log('Success');
          })
          .catch((err) => {
            console.log(err.message);
          });
      }