wonday / react-native-pdf

A <Pdf /> component for react-native

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Requires that you obtain access using ACTION_OPEN_DOCUMENT or related APIs

SitaMelark opened this issue · comments

React native 0.72.4, react native pdf ^6.7.1 and android platform

When i try show a pdf in the view by using the uri, i get this exception. Seems to be permissions problem but already got external storage permission already.

Join a screenshot or video of the problem on the simulator or device?
error
permissions

This is part of my code:

import { StackNavigationProp } from "@react-navigation/stack";
import { FlatList, Image, PermissionsAndroid, Pressable, SafeAreaView, ScrollView, StyleSheet, Text, TouchableOpacity, View } from "react-native";
import { useNavigation } from "@react-navigation/native";
import { useEffect, useState } from "react";
import FontAwesome5 from "react-native-vector-icons/FontAwesome5";
import { launchCamera, MediaType, CameraType, launchImageLibrary } from 'react-native-image-picker';
import { isCancel, pick, types } from 'react-native-document-picker';
import { readFile } from 'react-native-fs';
import Pdf from 'react-native-pdf';

export type StackParamList = {

  Profile: { userId: string };
  Balance: undefined;
  Login: undefined;
};

type Props = {

  navigation: StackNavigationProp<StackParamList, 'Profile'>;

}


function App({ navigation }: Props): JSX.Element {

  const [doc, setDoc] = useState("");
  const [nameDoc, setNameDoc] = useState("");
  const [tipoDoc, setTipoDoc] = useState("");
  const [base64Doc, setBase64Doc] = useState("");



  const options = {
    maxWidth: 400,
    maxHeight: 400,
    mediaType: 'photo' as MediaType,
    cameraType: 'back' as CameraType,
    selectionLimit: 1,
    saveToPhotos: false,
    includeBase64: true,
  };

  const documentosDisponible = [types.doc, types.csv, types.docx, types.images, types.pdf, types.plainText, types.xls, types.xlsx]

  const abrirGaleria = async () => {

    try {
      const granted = await PermissionsAndroid.request(
        PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE,
        {
          title: 'Storage Permission',
          message: 'App needs access to your storage to read the PDF file.',
          buttonNegative: 'Cancel',
          buttonPositive: 'OK',
        },
      );
      if (granted === PermissionsAndroid.RESULTS.GRANTED) {
        try {
          const result = await pick({
            type: documentosDisponible,
          });
          console.log(result);
          if (result[0] !== undefined) {
            const tipo = result[0].type;
            const doc = result[0].uri;
            const name = result[0].name;
            if (tipo !== undefined && tipo !== null && doc !== undefined && name !== null) {

              try {
                const fileContent = await readFile(doc, 'base64');
                setBase64Doc(fileContent);
                setTipoDoc(tipo);
                setNameDoc(name);
                if (tipo.includes("text")) {
                  try {
                    const content = await readFile(doc, 'utf8');
                    setDoc(content);
                    console.log(content);
                  } catch (error) {
                    console.error('Error reading file:', error);
                  }
                } else {
                  setDoc(doc);
                }
              } catch (error) {
                console.error('Error al convertir a base64:', error);
              }
            }
          }

        } catch (err) {
          if (isCancel(err)) {
            console.log("Cancelado");
          } else {
            console.log("Error: ", err)
          }
        }
      } else {
        console.log('Acceso a almacenamiento denegado');
      }
    } catch (err) {
      console.warn(err);
    }
  }

  return (
    <View style={styles.body}>
      <ScrollView contentContainerStyle={styles.scrollContainer}>
        <View style={styles.container}>
          <Text style={styles.title}>Subir imagen</Text>

          <View>
            <TouchableOpacity style={styles.buttonContainer} onPress={abrirGaleria}>
              <FontAwesome5 name={'file-import'} size={30} color={'white'} />
            </TouchableOpacity>
          </View>

          {doc !== "" && <View style={{ top: 10 }}>
            <View>
              {tipoDoc.includes("image") && <View style={styles.txtDisplay}>
                <Image
                  style={{ width: '100%', height: '100%', alignSelf: 'center' }}
                  source={{ uri: doc }}
                />
              </View>}
              {tipoDoc.includes("text") && <View style={styles.txtDisplay}>
                <ScrollView>
                  <Text>{doc}</Text>
                </ScrollView>
              </View>}
              {tipoDoc.includes("pdf") && <View style={styles.txtDisplay}>
                <Pdf source={{uri: doc}}/>
              </View>}
              
            <View style={{ alignItems: 'center' }}>
              <TouchableOpacity style={styles.redButtonContainer}>
                <Text style={styles.buttonText}>Subir documento</Text>
              </TouchableOpacity>
              <TouchableOpacity style={styles.redButtonContainer} onPress={() => { setDoc(""); setTipoDoc(""); setBase64Doc(""); }}>
                <Text style={styles.buttonText}>Descartar documento</Text>
              </TouchableOpacity>
            </View>
          </View>}
        </View>
      </ScrollView>
    </View>
  );
}

const styles = StyleSheet.create({
  body: {
    width: '100%',
    height: '100%',
  },
  container: {
    alignItems: 'center',
    height: '100%',
    width: '100%',
  },
  scrollContainer: {
    flexGrow: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  title: {
    fontSize: 24,
    fontWeight: 'bold',
    color: 'black',
    marginTop: 20,
    marginBottom: 5,
  },
  buttonContainer: {
    backgroundColor: '#feb600',
    padding: 10,
    borderRadius: 5,
    margin: 5,
  },
  buttonText: {
    fontSize: 16,
    fontWeight: 'bold',
    color: 'white',
  },
  whiteButtonContainer: {
    justifyContent: 'center',
    width: 250,
    paddingLeft: 5,
    paddingRight: 5,
    height: 30,
    backgroundColor: 'white',
    borderWidth: 1,
    borderColor: 'black',
    borderRadius: 5,
    marginTop: 5,
    marginBottom: 10,
  },
  whiteButtonText: {
    fontSize: 16,
    color: 'black',
  },
  label: {
    fontSize: 14,
    marginBottom: 1,
  },
  redButtonContainer: {
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#8F0917',
    paddingRight: 10,
    paddingLeft: 10,
    borderRadius: 5,
    marginTop: 10,
    height: 35,
    width: 250,
  },
  dropdown: {
    margin: 5,
    height: 25,
    width: 250,
    borderColor: '#8F0917',
    borderWidth: 1,
    borderRadius: 3,
    justifyContent: 'center',
    flexDirection: 'row',
    display: 'flex',
    alignItems: 'center',
    zIndex: 2,
  },
  selectedText: {
    fontSize: 12,
    flex: 1,
    left: 3,
  },
  icon: {
    right: 3,
  },
  list: {
    width: '100%',
    backgroundColor: 'white',
    maxHeight: 150,
    position: 'absolute',
    borderRadius: 3,
    borderColor: '#8F0917',
    borderWidth: 1,
    top: 0,
    zIndex: 3,
  },
  item: {
    padding: 5,
  },
  selectedItem: {
    backgroundColor: 'lightgray',
  },
  itemText: {
    fontSize: 13,
  },
  image: {
    maxWidth: 200,
    maxHeight: 200,
  },
  txtDisplay: {
    alignSelf: 'center',
    backgroundColor: "rgba(122, 122, 122, .2)",
    borderWidth: 1,
    borderRadius: 5,
    borderColor: '#8F0917',
    height: 260,
    width: 300,
    padding: 5,
  }
});
export default App;

Ok so the issue was that react-native-pdf doesnt allow uri, so i just used base64 and now works.

@SitaMelark Could you explain how you did it, I have the same issue. Thanks.

  • The problem is due to the access restriction imposed by Android.

https://developer.android.com/about/versions/11/privacy/storage?hl=en

  • I fixed the issue by copying the file to the app cachesDirectory :

const ToggleFiles = () => { DocumentPicker.pick({ type: [DocumentPicker.types.pdf], allowMultiSelection:true, copyTo:'cachesDirectory', }).then(res=>{ console.log({res}); console.log(res[0].uri); setFiles(res) }).catch(err=>console.error({err})) }

  • And then pass fileCopyUri instead of uri to uri, but also use decodeURIComponent for files with non-latin names (utf-8) :

<Pdf singlePage fitPolicy={0} source={{uri:decodeURIComponent(file.fileCopyUri)}} onError={err=>console.log('FETCHING FILE::ERR ==>',err)} style={{borderRadius:5,flex:1}}/>