3

I have to let user download a pdf file whene he clock on button, I find that I have to use rn-fetch-blob instead of react-native-fetch-blob. In the documentation there is this code:

const { config, fs } = RNFetchBlob
let DownloadDir = fs.dirs.DownloadDir // this is the Downloads directory.
let options = {
fileCache: true,
addAndroidDownloads : {
useDownloadManager : true, //uses the device's native download manager.
notification : true,
title : "Notification Title", // Title of download notification.
path: DownloadDir + "/me_"+ '.' + extension, // this is the path where your download file will be in
description : 'Downloading file.'
}
}
config(options)
.fetch('GET',"https://whatever_url_u _want/)
.then((res) => {
//console.log("Success");
})
.catch((err) => {console.log('error')}) // To execute when download cancelled and other errors
}

I have no idea what I can do with this ! how to use use it in TouchableOpacity onPress prop ? please someone can provide a detailed example

PS. I call an API with POST methode and I receive a link of PDF file. I think

I have to set this link like that
    config(options)
    .fetch('GET',this.state.data.link)
zedArt
  • 487
  • 1
  • 10
  • 27

2 Answers2

19
  1. Add below permissions in AndroidManifest.xml
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" />
  1. To use downloadmanager, add below action in intent in AndroidManifest.xml
<intent-filter>
     <action android:name="android.intent.action.MAIN" />
     <category android:name="android.intent.category.LAUNCHER" />
     <action android:name="android.intent.action.DOWNLOAD_COMPLETE"/>                          
</intent-filter>
  1. import PermissionsAndroid, Alert from react native (android only)
import {PermissionsAndroid, Alert} from "react-native";
  1. Now in component
actualDownload = () => {
   const { dirs } = RNFetchBlob.fs;
  RNFetchBlob.config({
    fileCache: true,
    addAndroidDownloads: {
    useDownloadManager: true,
    notification: true,
    mediaScannable: true,
    title: `test.pdf`,
    path: `${dirs.DownloadDir}/test.pdf`,
    },
  })
    .fetch('GET', 'http://www.africau.edu/images/default/sample.pdf', {})
    .then((res) => {
      console.log('The file saved to ', res.path());
    })
    .catch((e) => {
      console.log(e)
    });
}

downloadFile = () => {
  try {
      const granted = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE);
      if (granted === PermissionsAndroid.RESULTS.GRANTED) {
        this.actualDownload();
      } else {
        Alert.alert('Permission Denied!', 'You need to give storage permission to download the file');
      }
    } catch (err) {
      console.warn(err);
    } 
}

render(){
  <TouchableOpacity onPress={this.downloadFile}>
    <Text>Download!!!</Text>
  </TouchableOpacity>
}

CAUTION: You need to ask for storage permission for android 6 or higher in runtime

mosabbir tuhin
  • 560
  • 4
  • 14
  • thanks for your response but I tried your code without any modification but when I run my application I have an error : unsupported path /data/data/com.nameOfMyApp/files/test.pdf - this is the error, I'm a real beginner so can you help more ? – zedArt Jul 12 '19 at 14:16
  • I have updated my answer, try to save in DownloanDir not DocumentDir **`${dirs.DownloadDir}/test.pdf`** – mosabbir tuhin Jul 13 '19 at 21:24
  • 1
    I tried this too I have a notification: Download unsuccesful. ans when I console.warn the error I have that: Download manager failerd to download from http.... Statut code = 16 – zedArt Jul 15 '19 at 11:41
  • same thing with your code , it gave me an error when I console.warn it: line:160418, column: 34, sourceURL: http://10.0.2.2:8081/index.delta?platform=android&dev=true&minify=false – zedArt Jul 15 '19 at 11:48
  • 1
    It's okey when I generate an APK it works fine on mobile I have just have set somthing to open file directly when I click on notification of downloading – zedArt Jul 17 '19 at 12:55
  • can you please tell me how ask for storage because I have to do it manually and I want it to be automatically with an alert message and allow button like camera... and how can I allow to user to download file many times – zedArt Jul 22 '19 at 10:36
  • @zedArt, i have changed my answers according to your need, hopefully this will work for you. – mosabbir tuhin Jul 23 '19 at 17:26
  • thank you @mosabbir tuhin. Can you help me to let user download file many time because I got an error when I clock on download button second time. or can I let user open file directly the second time he click on the button ? – zedArt Jul 24 '19 at 09:17
  • Shouldn't function `downloadFile` be async? You may run into some issues without that. – Vojtěch Vokálek Feb 06 '20 at 10:24
  • In iOS13, I had problems with the 'DownloadDir', so I changed to 'DocumentDir', and in my case works fine! – Carlos Irano May 28 '20 at 19:19
  • Hello, I have base64 string instead of PDF path. How can I save that PDF in my mobile storage. – Ahtesham Shah Mar 10 '21 at 07:47
  • If you have a API endpoint that gives you base64 string as response for PDF. How are you going to download it ? – Saroj Jan 25 '23 at 06:44
9

LATEST WORKING SOLN both ios/android

Follow mosabbir tuhin's answer and then use my function actualDownload() and permissionFunc() to make pdf work on ios also.

const permissionFunc = async () => {
    if (Platform.OS == 'ios') {
        actualDownload();
    } else {
        if (downloaded) {
            try {
                const granted = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE);
                if (granted === PermissionsAndroid.RESULTS.GRANTED) {
                    actualDownload();
                } else {
                    showSnackbar('You need to give storage permission to download the file');
                }
            } catch (err) {
                console.warn(err);
            }
        }
        else {
            showSnackbar('File is already downloaded.');
        }
    }
}

const actualDownload = () => {
    const { dirs } = RNFetchBlob.fs;
    const dirToSave = Platform.OS == 'ios' ? dirs.DocumentDir : dirs.DownloadDir
    const configfb = {
        useDownloadManager: true,
        notification: true,
        mediaScannable: true,
        title: pdfInfo.pdf,
        path: `${dirToSave}/${pdfInfo.pdf}`,
    }
    const configOptions = Platform.select({
        ios: {
            title: configfb.title,
            path: configfb.path,
            appendExt: 'pdf',
        },
        android: configfb,
    });

    console.log('The file saved to 23233', configfb, dirs);

    RNFetchBlob.config(configOptions)
        .fetch('GET', `https://aquatherm.s3.ap-south-1.amazonaws.com/pdfs/${pdfInfo.pdf}`, {})
        .then((res) => {
            if (Platform.OS === "ios") {
                RNFetchBlob.ios.previewDocument(configfb.path);
            }
            setisdownloaded(false)
            if (Platform.OS == 'android') {
                showSnackbar('File downloaded');
            }
            console.log('The file saved to ', res);
        })
        .catch((e) => {
            setisdownloaded(true)
            showSnackbar(e.message);
            console.log('The file saved to ERROR', e.message)
        });
}
Ajmal Hasan
  • 885
  • 11
  • 9
  • 1
    As we are already giving absolute path to save the file we shouldn't give fileCache property in the configOptions for ios.Also we can remove this line RNFetchBlob.fs.writeFile(configfb.path, res.data, 'base64'); – Dhruvil Shah Aug 23 '21 at 09:16