I'm using the library react-native-pdf to show it.
Its frame is okay because I've set the background in blue.
I'm doing a RNFetchBlob.fs.exists(path) just before and the value is true but nothing appears on pdf viewer
Any idea ? I have no log or whatever which tell me there is a problem.
<Pdf source={{uri : filePath}}
style={{backgroundColor: 'grey', flex : 1}}/>
Here is how I show the pdf.
const request = RNFetchBlob
.config(this.getConfig(title, ext))
.fetch('GET', uri, header);
this.setState(
() => {
return {request: request}
},
() => {
this.state.request.then((res) => {
if (Platform.OS === 'android') {
const path = res.path()
const infos = res.info()
console.log(infos)
if (path !== undefined){
this.setState(() => {
return {
filePath : path
}
})
The file path is returned by the res.path()
addAndroidDownloads: {
useDownloadManager: true,
notification: false,
path: filePath,
description: `${title}`,
mediaScannable: true,
}
The variable config is made like this.
Related
react native pdf file download after i have not open not for android
working for ios iPhone 13 device IOS 15.5
android Pixels 3a android 11v
i think i have added everything needed in android manifest,
It works for ios devices but not for android. after i download the file i can't open it i.e. there is a download error
my code:
import { Alert, PermissionsAndroid, Platform } from 'react-native';
import RNFetchBlob from 'rn-fetch-blob';
interface IDownloadOptions {
src: string;
fileName: string;
token: string | null;
setLoading?: (loading: boolean) => void;
}
// commint
class DownloadManager {
public download = async (options: IDownloadOptions): Promise<any> => {
const { token, src, fileName, setLoading } = options;
const { dirs } = RNFetchBlob.fs;
const dirToSave = Platform.OS === 'ios' ? dirs.DocumentDir : dirs.DownloadDir;
console.log('dirToSave', dirToSave);
if (Platform.OS === 'android') {
const permission = await PermissionsAndroid.request('android.permission.WRITE_EXTERNAL_STORAGE');
if (permission !== 'granted') {
console.log('not granted');
setLoading && setLoading(false);
return;
}
}
const ios = RNFetchBlob.ios;
const android = RNFetchBlob.android;
const configfb = {
fileCache: true,
useDownloadManager: true,
notification: true,
mediaScannable: true,
title: fileName,
path: `${dirToSave}/${fileName}`,
};
console.log('config', configfb);
return new Promise((resolve, reject) => {
requestAnimationFrame(() => {
RNFetchBlob.config({
fileCache: configfb.fileCache,
// #ts-ignore
title: configfb.title,
path: configfb.path,
// #ts-ignore
appendExt: 'pdf',
addAndroidDownloads: {
...configfb,
},
})
.fetch('GET', src, {
Authorization: `Bearer ${token}`,
})
.then(res => {
const status = res.info().status;
console.log('status: ' + status);
console.log('res: ' + res);
if (status === 200) {
if (Platform.OS === 'ios') {
ios.openDocument(res.data);
}
if (Platform.OS === 'android') {
android.actionViewIntent(res.path(), 'pdf');
}
}
if (status === 500) {
Alert.alert('Server error 500');
}
setLoading && setLoading(false);
resolve(res);
})
.catch(error => {
console.log('error', error);
setLoading && setLoading(false);
reject(error);
});
});
});
};
}
Here is my code:
const saveImg = async (base64Img: string, success: Function, fail:Function) => {
const isAndroid = Platform.OS === "android"
const isIos = Platform.OS === 'ios'
const dirs = isIos? RNFS.LibraryDirectoryPath : RNFS.ExternalDirectoryPath;
const certificateTitle = 'certificate-'+((Math.random() * 10000000) | 0)
const downloadDest = `${dirs}/${certificateTitle}.png`;
const imageDatas = base64Img.split('data:image/png;base64,');
const imageData = imageDatas[1];
try{
await RNFetchBlob.config({
addAndroidDownloads:{
notification:true,
description:'certificate',
mime:'image/png',
title:certificateTitle +'.png',
path:downloadDest
}
}).fs.writeFile(downloadDest, imageData, 'base64')
if (isAndroid) {
} else {
RNFetchBlob.ios.previewDocument(downloadDest);
}
success()
}catch(error:any){
console.log(error)
fail()
}
}
I get this error:
undefined is not an object (near '...}).fs.writeFile(downloadD...')
at node_modules/react-native-webview/lib/WebView.android.js:207:16 in _this.onMessage
When I hit the download button and this runs I get the mentioned Error.
I use to get the download done with the below code modification, but I really need to show the download feedback from both android and IOS.
This works (but without notification)
await RNFetchBlob.fs.writeFile(downloadDest, imageData, 'base64')
I am using expo
I discovered that the react-fetch-blob does not work with expo, to solve it, I used the following libraries:
expo-file-system, expo-media-library, expo-image-picker,expo-notifications
This was the code to convert, download and show the notification of the image in the "expo way":
import * as FileSystem from 'expo-file-system';
import * as MediaLibrary from 'expo-media-library';
import * as ImagePicker from 'expo-image-picker';
import * as Notifications from 'expo-notifications';
const saveImg = async (base64Img: string, success: Function, fail:Function) => {
const imageDatas = base64Img.split('data:image/png;base64,');
const imageData = imageDatas[1];
try {
const certificateName = 'certificate-'+((Math.random() * 10000000) | 0) + ".png"
const certificatePathInFileSystem = FileSystem.documentDirectory +certificateName ;
await FileSystem.writeAsStringAsync(certificatePathInFileSystem, imageData, {
encoding: FileSystem.EncodingType.Base64,
});
await MediaLibrary.saveToLibraryAsync(certificatePathInFileSystem);
Notifications.setNotificationHandler({
handleNotification: async () => ({
shouldShowAlert: true,
shouldPlaySound: false,
shouldSetBadge: true,
}),
});
await Notifications.scheduleNotificationAsync({
content: {
title: certificateName +' saved !',
body: "Click to show the certificate",
},
trigger: null,
});
setCertificatePath(certificatePathInFileSystem)
success()
} catch (e) {
console.error(e);
fail()
}
}
In order to open the images gallery on click I used this code:
useEffect(()=>{
if(certificatePath){
Notifications.addNotificationResponseReceivedListener( async (event )=> {
await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.All,
allowsEditing: true,
})
})
}
},[certificatePath])
Try to call fetch after create RNFetchBlob.config
If you just wanna display an Image and not store you can show image as fallows (https://reactnative.dev/docs/next/images#uri-data-images)
<Image
style={{
width: 51,
height: 51,
resizeMode: 'contain'
}}
source={{
uri: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADMAAAAzCAYAAAA6oTAqAAAAEXRFWHRTb2Z0d2FyZQBwbmdjcnVzaEB1SfMAAABQSURBVGje7dSxCQBACARB+2/ab8BEeQNhFi6WSYzYLYudDQYGBgYGBgYGBgYGBgYGBgZmcvDqYGBgmhivGQYGBgYGBgYGBgYGBgYGBgbmQw+P/eMrC5UTVAAAAABJRU5ErkJggg=='
}}
/>
Call fetch on config object:
try{
const fetchConfig = await RNFetchBlob.config({
addAndroidDownloads:{
notification:true,
description:'certificate',
mime:'image/png',
title:certificateTitle +'.png',
path:downloadDest
}
})
fetchConfig.fetch('your.domain.com').fs.writeFile(downloadDest, imageData, 'base64')
if (isAndroid) {
} else {
RNFetchBlob.ios.previewDocument(downloadDest);
}
success()
}catch(error:any){
console.log(error)
fail()
}
I used react-native-fs to download an image from the server then I used openCropper() from react-native-image-crop-picker to crop. It worked well in iOS, however, it crashed in Android running without any error message or alert.
Have you ever in this issue ? Give me a guide. Thanks everyone.
const onCropImage = async () => {
setViewImageModal(false);
const uri = `${RNFS.DocumentDirectoryPath}/${fileName}`;
let options = {
fromUrl: viewImage && viewImage[0].url,
toFile: uri,
};
await RNFS.downloadFile(options).promise;
// App crashed when I call .openCropper() in Android running
ImageCropPicker.openCropper({
path: uri,
width: 300,
height: 400,
cropping: true,
freeStyleCropEnabled: true,
})
.then((image) => {
if (image) {
const temp = image.path.split("/");
const imageName = temp[temp.length - 1];
navigation.navigate("EditHostScreen", {
host,
type: "hosts",
info: { uri: image.path, typeImage: image.mime, name: imageName },
});
const time = setTimeout(() => {
ImageCropPicker.clean();
if (uri) {
RNFS.unlink(uri);
}
}, 100000);
clearTimeout(time);
}
})
.catch((err) => {
// console.log(err);
});
};
You need to add the android specific path value.
import { Platform } from 'react-native';
ImageCropPicker.openCropper({
path: Platform.OS === "android" ('file://' + uri) : uri,
width: 300,
height: 400,
cropping: true,
freeStyleCropEnabled: true,
})
I'm using the following code to download a file (can be a PDF or a DOC) and then opening it using Linking.
const { dirs } = RNFetchBlob.fs;
let config = {
fileCache : true,
appendExt : extension,
addAndroidDownloads : {
useDownloadManager : false,
notification : false,
title : 'File',
description : 'A file.',
path: `${dirs.DownloadDir}/file.${extension}`,
},
};
RNFetchBlob.config(config)
.fetch(
method,
remoteUrl,
APIHelpers.getDefaultHeaders()
)
.then((res) => {
let status = res.info().status;
if (status == 200) {
Linking.canOpenURL(res.path())
.then((supported) => {
if (!supported) {
alert('Can\'t handle url: ' + res.path());
} else {
Linking.openURL(res.path())
.catch((err) => alert('An error occurred while opening the file. ' + err));
}
})
.catch((err) => alert('The file cannot be opened. ' + err));
} else {
alert('File was not found.')
}
})
.catch((errorMessage, statusCode) => {
alert('There was some error while downloading the file. ' + errorMessage);
});
However, I'm getting the following error:
An error occurred while opening the file. Error: Unable to open URL:
file:///Users/abhishekpokhriyal/Library/Developer/CoreSimulator/Devices/3E2A9C16-0222-40A6-8C1C-EC174B6EE9E8/data/Containers/Data/Application/A37B9D69-583D-4DC8-94B2-0F4AF8272310/Documents/RNFetchBlob_tmp/RNFetchBlobTmp_o259xexg7axbwq3fh6f4.pdf
I need to implement the solution for both iOS and Android.
I think the easiest way to do so is by using react-native-file-viewer package.
It allows you to Prompt the user to choose an app to open the file with (if there are multiple installed apps that support the mimetype).
import FileViewer from 'react-native-file-viewer';
const path = // absolute-path-to-my-local-file.
FileViewer.open(path, { showOpenWithDialog: true })
.then(() => {
// success
})
.catch(error => {
// error
});
So, I finally did this by replacing Linking by the package react-native-file-viewer.
In my APIHelpers.js:
async getRemoteFile(filePath, extension, method = 'GET') {
const remoteUrl = `${API_BASE_URL}/${encodeURIComponent(filePath)}`;
const { dirs } = RNFetchBlob.fs;
let config = {
fileCache : true,
appendExt : extension,
addAndroidDownloads : {
useDownloadManager : false,
notification : false,
title : 'File',
description : 'A file.',
path: `${dirs.DownloadDir}/file.${extension}`,
},
};
return new Promise(async (next, error) => {
try {
let response = await RNFetchBlob.config(config)
.fetch(
method,
remoteUrl,
this.getDefaultHeaders()
);
next(response);
} catch (err) {
error(err);
}
});
}
In my Actions.js
export function openDocument(docPath, ext) {
return async (dispatch) => {
dispatch(fetchingFile());
APIHelpers.getRemoteFile(docPath, ext).then(async function(response) {
dispatch(successFetchingFile());
let status = response.info().status;
if (status == 200) {
const path = response.path();
setTimeout(() => {
FileViewer.open(path, {
showOpenWithDialog: true,
showAppsSuggestions: true,
})
.catch(error => {
dispatch(errorOpeningFile(error));
});
}, 100);
} else {
dispatch(invalidFile());
}
}).catch(function(err) {
dispatch(errorFetchingFile(err));
});
}
}
In my Screen.js
import { openDocument } from 'path/to/Actions';
render() {
return <Button
title={'View file'}
onPress={() => this.props.dispatchOpenDocument(doc.filepath, doc.extension)}
/>;
}
.
.
.
const mapDispatchToProps = {
dispatchOpenDocument: (docPath, ext) => openDocument(docPath, ext),
}
Are you downloading it from the web? I can see the pdf path is attached at the end of the error path.
For web URLs, the protocol ("http://", "https://") must be set accordingly!
Try to append appropriate schemes to your path. Check it out from the link mentioned below.
This can be done with 'rn-fetch-blob'
RNFetchBlob.android.actionViewIntent(fileLocation, mimeType);
I need to show custom icons in my places, that icons are loaded from a specific URL dynamically. Here is my code.
const PlacesMarker = props => {
const { places } = props
const [geoJson, setGeoJson] = useState([])
useEffect(() => {
if (places)
renderPlaces(places)
}, [places])
const renderPlaces = (places) => {
let features = places.content.map((item) => {
return {
type: "Feature",
id: item.id,
properties: {
id: item.id,
icon: item.type.iconUri,
name: item.name,
type: item.type.name
},
geometry: {
type: "Point",
coordinates: [item.location.lon, item.location.lat],
},
}
})
setGeoJson(features)
}
return (
<View>
{geoJson.length > 0 ?
<MapboxGL.ShapeSource id={'places-map'}
shape={{ type: "FeatureCollection", features: geoJson }}>
<MapboxGL.SymbolLayer
id={Math.random().toString()}
style={{
iconImage: ['get', 'icon']
iconAllowOverlap: true,
// iconSize: 0.80,
iconIgnorePlacement: true,
textField: ['get', 'icon']
}}
/>
</MapboxGL.ShapeSource> : null
}
</View >
)
}
export default PlacesMarker
In the style, I used the expression 'get', and it works, because I set the textField with Icon URI value and it shows the uri. However if I set the iconImage property with the URI, then the icon appear successfully
In react-native-mapbox-gl 7.0 if iconImage is constant then you can use url-s. But if it's an expression it should be a key in images dict of Images, <Images images={images} />.
See https://github.com/react-native-mapbox-gl/maps/issues/652