skip to Main Content

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

2

Answers


  1. Chosen as BEST ANSWER

    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])
    

    1. Try to call fetch after create RNFetchBlob.config
    2. If you just wanna display an Image and not store you can show image as fallows (https://reactnative.dev/docs/next/images#uri-data-images)
    <Image
      style={{
        width: 51,
        height: 51,
        resizeMode: 'contain'
      }}
      source={{
        uri: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADMAAAAzCAYAAAA6oTAqAAAAEXRFWHRTb2Z0d2FyZQBwbmdjcnVzaEB1SfMAAABQSURBVGje7dSxCQBACARB+2/ab8BEeQNhFi6WSYzYLYudDQYGBgYGBgYGBgYGBgYGBgZmcvDqYGBgmhivGQYGBgYGBgYGBgYGBgYGBgbmQw+P/eMrC5UTVAAAAABJRU5ErkJggg=='
      }}
    />
    

    Call fetch on config object:

            try{
                const fetchConfig = await RNFetchBlob.config({
                    addAndroidDownloads:{
                        notification:true,
                        description:'certificate',
                        mime:'image/png',
                        title:certificateTitle +'.png',
                        path:downloadDest
                    }
                })
        fetchConfig.fetch('your.domain.com').fs.writeFile(downloadDest, imageData, 'base64')
                if (isAndroid) {
                  } else {
                    RNFetchBlob.ios.previewDocument(downloadDest);
                  }
                success()
            }catch(error:any){
                console.log(error)
                fail()
            }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search