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?
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}}/>