Saroj
Saroj

Reputation: 1571

Expo React Native download a pdf file from server and save it in the local file system

I am having a hard time understanding the process of downloading a file pdf or excel from let's say a nodejs server in ReactNative using Expo.

So far I have gone through a bunch of blog posts/resources on the web but unfortunately I didn't find any solution for this. Most of the blogs are outdated or they don't have any clear implementation for it.

Here are a few approach that I came across.

  1. using rn-fetch-blob but this does not work with expo as per my findings and each time I tried to integrate it with Expo, I ended up with error.

  2. The second approach to use expo's FileSharing approach, which kind of works but only if you have a predefined image or pdf file.


import { StatusBar } from "expo-status-bar";
import { useEffect, useState } from "react";
import { StyleSheet, Text, View, Button, Alert } from "react-native";
import * as FileSystem from "expo-file-system";
import * as Sharing from "expo-sharing";

export default function App() {
  const [downloadProgress, setDownloadProgress] = useState(0);
  const [document, setDocument] = useState(null);

  async function openShareDialogAsync() {
    if (!(await Sharing.isAvailableAsync())) {
      alert(`Uh oh, sharing isn't available on your platform`);
      return;
    }

    Sharing.shareAsync(document);
  }

  async function handleDownload() {
    const callback = (downloadProgress) => {
      const progress =
        downloadProgress.totalBytesWritten /
        downloadProgress.totalBytesExpectedToWrite;
      setDownloadProgress(progress * 100);
    };
    let url =
      "https://www.adobe.com/support/products/enterprise/knowledgecenter/media/c4611_sample_explain.pdf";

    const downloadResumable = FileSystem.createDownloadResumable(
      url,
      FileSystem.documentDirectory + "a.pdf",
      {},
      callback
    );

    try {
      const { uri } = await downloadResumable.downloadAsync();
      console.log("Finished downloading to ", uri);
      setDocument(uri);
    } catch (e) {
      console.error(e);
    }
  }

  useEffect(() => {
    handleDownload();
  }, []);

  return (
    <View style={styles.container}>
      <Text>Open up App.js to start working on your app!</Text>
      <Text>Welcome Saroj!</Text>
      <Button title="Share" onPress={openShareDialogAsync} />
      <StatusBar style="auto" />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "#fff",
    alignItems: "center",
    justifyContent: "center",
  },
});

This implementation kind of works. I am able to share the pdf but the issue is the pdf link is not generated on the fly, it is something already being generated previosuly. I expect something like we can call an endpoint to a server and the server would either send base64 or binary data that we can use to make the pdf download to the local storage of the device.

I am open to suggestions and would really appreciate your inputs.

Upvotes: 2

Views: 2369

Answers (1)

user18309290
user18309290

Reputation: 8300

Use Blob and FileReader to download any binary data. Something like this:

const response = await fetch('<link>');
const data = await response.blob()

const reader = new FileReader();
reader.onload = async () => {
  const fileUri = FileSystem.documentDirectory + "file";
  await FileSystem.writeAsStringAsync(
    fileUri, reader.result.split(',')[1], 
    { encoding: FileSystem.EncodingType.Base64 }
  );
};
reader.readAsDataURL(data);

Upvotes: 1

Related Questions