androidiosreact-nativeexporeact-native-fetch-blob

I'm getting 'undefined is not an object (near '...}).fs.writeFile(' when trying to download base64 image png in react native React Native


Here is my code:

    const saveImg = async (base64Img: string, success: Function, fail:Function) => {
        const isAndroid = Platform.OS === "android"
        const isIos = Platform.OS === 'ios'

        const dirs = isIos? RNFS.LibraryDirectoryPath : RNFS.ExternalDirectoryPath;
        const certificateTitle = 'certificate-'+((Math.random() * 10000000) | 0)
        const downloadDest = `${dirs}/${certificateTitle}.png`;
        const imageDatas = base64Img.split('data:image/png;base64,');
        const imageData = imageDatas[1];

        try{
            await RNFetchBlob.config({
                addAndroidDownloads:{
                    notification:true,
                    description:'certificate',
                    mime:'image/png',
                    title:certificateTitle +'.png',
                    path:downloadDest
                }
            }).fs.writeFile(downloadDest, imageData, 'base64')
            if (isAndroid) {
              } else {
                RNFetchBlob.ios.previewDocument(downloadDest);
              }
            success()
        }catch(error:any){
            console.log(error)
            fail()
        }
    }

I get this error:

undefined is not an object (near '...}).fs.writeFile(downloadD...')
at node_modules/react-native-webview/lib/WebView.android.js:207:16 in _this.onMessage

When I hit the download button and this runs I get the mentioned Error. I use to get the download done with the below code modification, but I really need to show the download feedback from both android and IOS.

This works (but without notification)

await RNFetchBlob.fs.writeFile(downloadDest, imageData, 'base64')

I am using expo


Solution

  • I discovered that the react-fetch-blob does not work with expo, to solve it, I used the following libraries:

    expo-file-system, expo-media-library, expo-image-picker,expo-notifications
    

    This was the code to convert, download and show the notification of the image in the "expo way":

    import * as FileSystem from 'expo-file-system';
    import * as MediaLibrary from 'expo-media-library';
    import * as ImagePicker from 'expo-image-picker';
    import * as Notifications from 'expo-notifications';
    
    const saveImg = async (base64Img: string, success: Function, fail:Function) => {
    
        const imageDatas = base64Img.split('data:image/png;base64,');
        const imageData = imageDatas[1];
    
          try {
            const certificateName = 'certificate-'+((Math.random() * 10000000) | 0) + ".png"
            const certificatePathInFileSystem = FileSystem.documentDirectory +certificateName ;
            await FileSystem.writeAsStringAsync(certificatePathInFileSystem, imageData, {
              encoding: FileSystem.EncodingType.Base64,
            });
            await MediaLibrary.saveToLibraryAsync(certificatePathInFileSystem);
    
            Notifications.setNotificationHandler({
                handleNotification: async () => ({
                  shouldShowAlert: true,
                  shouldPlaySound: false,
                  shouldSetBadge: true,
    
                }),
              });
    
            await Notifications.scheduleNotificationAsync({
                content: {
                    title: certificateName +' saved !',
                    body: "Click to show the certificate",
                },
                trigger: null,
              });
              setCertificatePath(certificatePathInFileSystem)
          success()
          } catch (e) {
            console.error(e);
          fail()
          }
    }
    

    In order to open the images gallery on click I used this code:

    useEffect(()=>{
        if(certificatePath){
            Notifications.addNotificationResponseReceivedListener( async (event )=> {
                await ImagePicker.launchImageLibraryAsync({
                    mediaTypes: ImagePicker.MediaTypeOptions.All,
                    allowsEditing: true,
                  })
            })
        }
    },[certificatePath])