View pdf from URL in Expo React Native Project
Asked Answered
M

5

8

I have been trying to open a pdf file using react native, but every library that I try generates an error. I tried the code below:

import React, { Component } from 'react';
import Pdf from 'react-native-pdf';

class OpenBGReport extends Component {
  render() {
    const source = {uri:'http://samples.leanpub.com/thereactnativebook-sample.pdf',cache:true};

    return (
      <View style={styles.container}>
        <Pdf
                    source={source}
                    onLoadComplete={(numberOfPages,filePath)=>{
                        console.log(`number of pages: ${numberOfPages}`);
                    }}
                    onPageChanged={(page,numberOfPages)=>{
                        console.log(`current page: ${page}`);
                    }}
                    onError={(error)=>{
                        console.log(error);
                    }}
                    style={styles.pdf}/>
      </View>
    );
  }
}

const styles = StyleSheet.create({
    container: {
        flex: 1,
        justifyContent: 'flex-start',
        alignItems: 'center',
        marginTop: 25,
    },
    pdf: {
        flex:1,
        width:Dimensions.get('window').width,
        height:Dimensions.get('window').height,
    }
});

export default OpenBGReport;

However, after installing react-native-pdf, my project stop working and returns: null is not an object (evaluating 'rnfetchblob.documentdir')

What is the best way to open and display a pdf from a URL in react native?

Thanks

Millennium answered 18/10, 2019 at 16:58 Comment(1)
Did you find an answer without ejecting from Expo ?Boden
P
4

The best way I found to handle PDF files with Expo is to use buffer to turn them into base64 files, store them in the FileSystem and Share them:

  import { Buffer } from "buffer";
  import * as FileSystem from "expo-file-system";
  import * as Sharing from "expo-sharing";

  res = await // fetch PDF

  const buff = Buffer.from(res, "base64");
  const base64 = buff.toString("base64");

  const fileUri =
    FileSystem.documentDirectory + `${encodeURI("name-of-the-pdf")}.pdf`;


  await FileSystem.writeAsStringAsync(fileUri, base64, {
    encoding: FileSystem.EncodingType.Base64,
  });


  Sharing.shareAsync(res);

Note: You need to install buffer, expo-file-system and expo-sharing

Pentha answered 31/10, 2022 at 14:1 Comment(1)
This is the only solution that worked for me, thank you. Though at the end you need to share the fileUri and not res, so it's Sharing.shareAsync(fileUri) Full code being: const buff = Buffer.from(s.base64, 'base64') const base64 = buff.toString('base64') const path = FileSystem.documentDirectory + `${encodeURI('atmos')}.pdf` await FileSystem.writeAsStringAsync(path, base64, { encoding: FileSystem.EncodingType.Base64 }) Sharing.shareAsync(path)Flotsam
E
15

Expo doesn't support installing native modules and react-native-pdf is a native module. Possible solutions:

  1. If you really want to use native modules, you should use react-native-cli or eject expo project. You can find a more detailed answer in the repository FAQ section (https://github.com/wonday/react-native-pdf#faq).

  2. You can always open PDF files in the phone browser by using react-native's Linking API.

  3. There is a package to display PDF files in expo projects (https://github.com/xcarpentier/rn-pdf-reader-js). Never used the package, and it also seems very sketchy as it supports only the Android platform, and on iOS, you would need to use WebView to display pdf. Also, it doesn't have a lot of downloads on npm and the project itself is prettry stale.

Elenoraelenore answered 18/10, 2019 at 18:44 Comment(5)
I thought about using the phone browser with Linking, but it will show the URL to the user. I can't show the URLMillennium
Then there is no easy solution if you are using expo unless you want to eject your project :<Elenoraelenore
I had a bad experience ejecting an expo project lol. Is there a way to use something like an iFrame? So I can hide the URLMillennium
I think most of the people have a bad experience with ejecting expo projects. I don't know if there is any other way to hack around this issue, I usually use react-native-cli, to avoid those kinds of problems.Elenoraelenore
This answer has not aged well. 😁 Check out Expo, EAS Build and Managed Workflow.Latvian
T
4

You can use <WebView/> from "react-native-webview" (https://github.com/react-native-webview/react-native-webview) I tested this one on ios with expo, and it's work!

<WebView 
originWhitelist={['*']} 
source={{uri: uri}}

url can be a web link or path to your file local ('file:///...')

Tema answered 29/12, 2021 at 16:4 Comment(1)
Sadly, on my phone it downloads the pdf instead of showing it.Farinose
P
4

The best way I found to handle PDF files with Expo is to use buffer to turn them into base64 files, store them in the FileSystem and Share them:

  import { Buffer } from "buffer";
  import * as FileSystem from "expo-file-system";
  import * as Sharing from "expo-sharing";

  res = await // fetch PDF

  const buff = Buffer.from(res, "base64");
  const base64 = buff.toString("base64");

  const fileUri =
    FileSystem.documentDirectory + `${encodeURI("name-of-the-pdf")}.pdf`;


  await FileSystem.writeAsStringAsync(fileUri, base64, {
    encoding: FileSystem.EncodingType.Base64,
  });


  Sharing.shareAsync(res);

Note: You need to install buffer, expo-file-system and expo-sharing

Pentha answered 31/10, 2022 at 14:1 Comment(1)
This is the only solution that worked for me, thank you. Though at the end you need to share the fileUri and not res, so it's Sharing.shareAsync(fileUri) Full code being: const buff = Buffer.from(s.base64, 'base64') const base64 = buff.toString('base64') const path = FileSystem.documentDirectory + `${encodeURI('atmos')}.pdf` await FileSystem.writeAsStringAsync(path, base64, { encoding: FileSystem.EncodingType.Base64 }) Sharing.shareAsync(path)Flotsam
W
1

I suggest using react-native-file-viewer to open PDF files directly from React native. I tried to use the webview but it doesn't work for the local files on the device.

In my case I was selecting a file and then opening it. So I simply sent the fileCopyUri to FileViewer from react-native-file-viewer and it works.

 <Pressable
    onPress={() => {
        FileViewer.open(fileCopyUri)
            .then(() => {
                console.log("Success");
             })
            .catch((_err) => {
                console.log(_err);
            });
         }}
 >
     <Text>PDF</Text>
 </Pressable>

Also in the latest versions of expo you don't need to worry about expo eject. Read Here

Whist answered 13/3 at 18:28 Comment(0)
P
0

I solved the issue by opening the PDF in the users web browser.

import React from 'react';
import { Button, Linking } from 'react-native';

const PermitViewer = ({ downloadUrl }) => {
  const handlePress = async () => {
    const supported = await Linking.canOpenURL(downloadUrl);

    if (supported) {
      await Linking.openURL(downloadUrl);
    } else {
      console.log(`Don't know how to open this URL: ${downloadUrl}`);
    }
  };

  return <Button title="Open PDF" onPress={handlePress} />;
};

export default PermitViewer;
Pellagra answered 13/6, 2023 at 11:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.