I am trying to download a pdf from the URL. I am using RNFetchBlob to download pdf from URLs. It is also getting downloaded successfully. But it doesn't open. It says invalid format. At first, I thought it might be because I was running in an emulator but I tried on a real device also it is also giving an invalid format.
Below is the code I am using:
const downloadFile = async (url, title) => {
const grantedstorage = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE);
if (grantedstorage === PermissionsAndroid.RESULTS.GRANTED) {
const { dirs } = RNFetchBlob.fs;
const dirToSave = Platform.OS == 'ios' ? dirs.DocumentDir : dirs.DownloadDir
const configfb = {
fileCache: true,
addAndroidDownloads: {
useDownloadManager: true,
notification: true,
mediaScannable: true,
title: title + ".pdf",
path: dirToSave + "/" + title + ".pdf",
mime : 'application/pdf',
}
}
const configOptions = Platform.select({
ios: {
fileCache: configfb.fileCache,
title: configfb.title,
path: configfb.path,
appendExt: 'pdf',
notification: configfb.notification
},
android: configfb,
});
RNFetchBlob.config(configOptions)
.fetch('GET', url,{})
.then((res) => {
if (Platform.OS === "ios") {
RNFetchBlob.fs.writeFile(configfb.path, res.data, 'base64');
RNFetchBlob.ios.previewDocument(configfb.path);
}
if (Platform.OS == 'android') {
alert('File downloaded');
}
console.log('The file saved to ', res.path());
})
.catch((e) => {
console.log('Catch ERROR', e.message)
});
} else if (grantedstorage === PermissionsAndroid.RESULTS.DENIED) {
alert("Please allow permission to storage if you want to download file.");
}
else {
alert("Please go to app setting and allow permission to storage.");
}
}
Please help me solve this. And yes the URL is of a protected pdf if that is of any concern, please let me know.
Related
I am trying to download image which is coming from api.I am using rn-fetch-blob package to download file. But the problem here is when i click on download file do get downloaded but it gives some error saying
Download manager donwload failed , the file does not downloaded to destination
Any help would be great.
This is how i implemented rn-fetch-blob in my code
const checkPermission = async (image: string) => {
if (Platform.OS === 'android') {
try {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE,
{
title: 'Storage Permission Required',
message: 'This app needs access to your storage to download Photos',
buttonPositive: 'OK',
},
);
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
console.log('Storage Permission Granted.');
handleDownload(image);
} else {
Alert.alert('Storage Permission Not Granted');
}
} catch (err) {
console.warn(err);
}
}
};
const getExtention = (filename: string) => {
//To get the file extension
return /[.]/.exec(filename) ? /[^.]+$/.exec(filename) : undefined;
};
// eslint-disable-next-line #typescript-eslint/no-unused-vars
const handleDownload = async (image: string) => {
let date = new Date();
let image_url = image;
let ext = getExtention(image_url);
const {config, fs} = RNFetchBlob;
let pictureDir = fs.dirs.PictureDir;
let options = {
fileCache: true,
addAndroidDownloads: {
useDownloadManager: true,
notification: true,
path:
pictureDir +
'/wallace_' +
Math.floor(date.getTime() + date.getSeconds() / 2) +
'.' +
ext,
description: 'Image',
},
};
config(options)
.fetch('GET', image_url)
.then((res) => {
console.log(res.path());
Alert.alert('Image Downloaded Successfully.');
})
.catch((err) => {
Alert.alert('Download Failed', err.message);
});
};
return <View>
<TouchableWithoutFeedback
onPress={() => handleShare(src.portrait)}>
<Text variant="buttonText">Share Image</Text>
</TouchableWithoutFeedback>
</View>
I trying to upload a file image to API in postman thats work fine but when a i try file image from ImagePicker didnot work.
I think doing something wrong when create formdata
Handler
ImagePicker.showImagePicker(optionsImagePicker, (response) => {
console.log('Response = ', response);
if (response.didCancel) {
console.log('User cancelled image picker');
} else if (response.error) {
console.log('ImagePicker Error: ', response.error);
} else if (response.customButton) {
console.log('User tapped custom button: ', response.customButton);
} else {
// const source = { image: response.data };
let photo = { uri: response.uri}
let formdata = new FormData();
formdata.append("product[name]", 'test')
formdata.append("product[price]", 10)
formdata.append("product[category_ids][]", 2)
formdata.append("product[description]", '12dsadadsa')
formdata.append("product[images_attributes[0][file]]", {uri: photo.uri, name: 'image.jpg', type: 'image/jpeg'})
updateProfilePic(formdata)
// You can also display the image using data:
// const source = { uri: 'data:image/jpeg;base64,' + response.data };
// this.setState({
// avatarSource: source,
// });
}
});
Service
export function uploadImageProfile(data: any): Promise<any> {
const config = {
headers: {
'Content-Type': 'multipart/form-data',
},
};
return api.post('/users/profilepic', {image: data}, config).then((res) => {
console.log(res.data);
return res.data;
});
}
Your form data must be like that.
formdata.append('file',{
uri: Platform.OS === 'android' ? photo.uri : 'file://' + photo.uri,
name: 'test',
type: 'image/jpeg' // or your mime type what you want
});
Then
axios.post('/users/profilepic', formdata, config).then((res) => {
console.log(res.data);
return res.data;
});
let formdata = new FormData();
formdata.append('file',{
uri: Platform.OS === 'android' ? photo.uri : 'file://' + photo.uri,
name: 'test',
type: 'image/jpeg'
});
use method:"POST" and spread formdata.getHeaders() into header
let reqObj = {
method: "POST",
url: 'http://example.com/upload/image',
headers: {
'x-sh-auth': token,
...formdata.getHeaders()
},
maxContentLength: Infinity,
maxBodyLength: Infinity
};
axios(reqObj).then(result => {
console.log(result)
}).catch(error => {
console.log(error)
});
I changed how I send image to the server. Now send im base64 and in server convert to file with fs.
const uploadImage = {
imageBase64: 'data:' + response.type + ';base64,' + response.data,
};
updateProfilePic(uploadImage);
server side
async saveImageProfile(imageBase64, logedUserData) {
let base64Image = imageBase64.imageBase64.split(';base64,').pop();
let type = imageBase64.imageBase64.split('image/').pop().split(';')[0];
let newFileName = `${logedUserData.id}.${type}`;
if (imageFileFilter(type)) {
const file = await fs.writeFile('./files/' + newFileName, base64Image, { encoding: 'base64' }, function (err) {
console.log('File created');
});
const url = `${baseUrl}/users/files/${newFileName}`;
this.updateRefProfilePic(url, logedUserData);
}
else {
throw new BadRequestException("Tipo de arquivo não suportado");
}
}
developers, I am using react-native-image-picker to upload images to the Nodejs server and MongoDB, when I select an image on react native app it is showing image fileName="null" on the console log. I am struggling for 3 weeks and couldn't find any solution. Below I have posted the console.log result:
Response = {"fileName": null, "fileSize": 13712705, "height": 3024, "isVertical": false, "origURL": "assets-library://asset/asset.HEIC?id=CC95F08C-88C3-4012-9D6D-64A413D254B3&ext=HEIC", "type": "image/jpeg", "uri": "file:///Users/shinobi/Library/Developer/CoreSimulator/Devices/9DBEA6D8-101E-4B9C-9DB0-D1CABA724AAF/data/Containers/Data/Application/44C0E455-2A43-40A3-A2EE-213A39B7743C/tmp/35A90028-55FA-4218-8FB7-34EB1DE62F58.jpg", "width": 4032}
Below is my react-native-image-picker code:
const ChangeTasbir = ({navigation, route}, props) => {
const [image, setImage] = useState(null);
const [id, setId] = useState(getDetails('id'));
const [isuploading, setIsuploading] = useState(false);
const selectImage = () => {
const options = {
noData: true,
};
ImagePicker.showImagePicker(options, response => {
console.log('Response = ', response);
if (response.didCancel) {
console.log('User cancelled image picker');
} else if (response.error) {
console.log('ImagePicker Error: ', response.error);
} else if (response.customButton) {
console.log('User tapped custom button: ', response.customButton);
} else {
const source = {
uri: response.uri,
fileName: response.fileName,
data: response.data,
};
// You can also display the image using data:
// const source = { uri: 'data:image/jpeg;base64,' + response.data };
setImage(source);
}
});
};
I'm using this options in my React native application on Android and works ok.
const options = {
mediaTypes: 'Images',
quality: 0.1,
};
ImagePicker.launchImageLibrary(options, (response) => {
if (response.didCancel !== true) {
this.setState({ profilePic: response, errorPic: false });
}
});
try this:
let path = response.uri;
if (Platform.OS === "ios") {
path = "~" + path.substring(path.indexOf("/Documents"));
}
if (!response.fileName) response.fileName = path.split("/").pop();
I have to let user download a pdf file whene he clock on button, I find that I have to use rn-fetch-blob instead of react-native-fetch-blob. In the documentation there is this code:
const { config, fs } = RNFetchBlob
let DownloadDir = fs.dirs.DownloadDir // this is the Downloads directory.
let options = {
fileCache: true,
addAndroidDownloads : {
useDownloadManager : true, //uses the device's native download manager.
notification : true,
title : "Notification Title", // Title of download notification.
path: DownloadDir + "/me_"+ '.' + extension, // this is the path where your download file will be in
description : 'Downloading file.'
}
}
config(options)
.fetch('GET',"https://whatever_url_u _want/)
.then((res) => {
//console.log("Success");
})
.catch((err) => {console.log('error')}) // To execute when download cancelled and other errors
}
I have no idea what I can do with this ! how to use use it in TouchableOpacity onPress prop ? please someone can provide a detailed example
PS. I call an API with POST methode and I receive a link of PDF file. I think
I have to set this link like that
config(options)
.fetch('GET',this.state.data.link)
Add below permissions in AndroidManifest.xml
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" />
To use downloadmanager, add below action in intent in AndroidManifest.xml
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<action android:name="android.intent.action.DOWNLOAD_COMPLETE"/>
</intent-filter>
import PermissionsAndroid, Alert from react native (android only)
import {PermissionsAndroid, Alert} from "react-native";
Now in component
actualDownload = () => {
const { dirs } = RNFetchBlob.fs;
RNFetchBlob.config({
fileCache: true,
addAndroidDownloads: {
useDownloadManager: true,
notification: true,
mediaScannable: true,
title: `test.pdf`,
path: `${dirs.DownloadDir}/test.pdf`,
},
})
.fetch('GET', 'http://www.africau.edu/images/default/sample.pdf', {})
.then((res) => {
console.log('The file saved to ', res.path());
})
.catch((e) => {
console.log(e)
});
}
downloadFile = () => {
try {
const granted = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE);
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
this.actualDownload();
} else {
Alert.alert('Permission Denied!', 'You need to give storage permission to download the file');
}
} catch (err) {
console.warn(err);
}
}
render(){
<TouchableOpacity onPress={this.downloadFile}>
<Text>Download!!!</Text>
</TouchableOpacity>
}
CAUTION: You need to ask for storage permission for android 6 or higher in runtime
LATEST WORKING SOLN both ios/android
Follow mosabbir tuhin's answer and then use my function actualDownload() and permissionFunc() to make pdf work on ios also.
const permissionFunc = async () => {
if (Platform.OS == 'ios') {
actualDownload();
} else {
if (downloaded) {
try {
const granted = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE);
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
actualDownload();
} else {
showSnackbar('You need to give storage permission to download the file');
}
} catch (err) {
console.warn(err);
}
}
else {
showSnackbar('File is already downloaded.');
}
}
}
const actualDownload = () => {
const { dirs } = RNFetchBlob.fs;
const dirToSave = Platform.OS == 'ios' ? dirs.DocumentDir : dirs.DownloadDir
const configfb = {
fileCache: true,
useDownloadManager: true,
notification: true,
mediaScannable: true,
title: pdfInfo.pdf,
path: `${dirToSave}/${pdfInfo.pdf}`,
}
const configOptions = Platform.select({
ios: {
fileCache: configfb.fileCache,
title: configfb.title,
path: configfb.path,
appendExt: 'pdf',
},
android: configfb,
});
console.log('The file saved to 23233', configfb, dirs);
RNFetchBlob.config(configOptions)
.fetch('GET', `https://aquatherm.s3.ap-south-1.amazonaws.com/pdfs/${pdfInfo.pdf}`, {})
.then((res) => {
if (Platform.OS === "ios") {
RNFetchBlob.fs.writeFile(configfb.path, res.data, 'base64');
RNFetchBlob.ios.previewDocument(configfb.path);
}
setisdownloaded(false)
if (Platform.OS == 'android') {
showSnackbar('File downloaded');
}
console.log('The file saved to ', res);
})
.catch((e) => {
setisdownloaded(true)
showSnackbar(e.message);
console.log('The file saved to ERROR', e.message)
});
}
I am working on a mobile app using React Native and posting an image on iOS doesn't work. I have hooked up my code to requestbin, setup the info.plist to allow non-https urls and other post requests are working (e.g login). For the image, all I get is a blank body for the request. Here is the code posting the image:
uploadImage = () => {
const data = new FormData();
data.append('photo', {
uri: this.state.logo.uri,
name: 'logo'
});
fetch([requestbin url here], {
method: 'post',
body: data
}).then(res => {
console.log(res);
});
for the image, I am using react-native-image-picker to get it and store it in state under the variable 'logo'. Here is that code as well
handleNewImage = () => {
var options = {
title: 'Choose new company logo',
storageOptions: {
skipBackup: true,
path: 'images'
}
};
showImagePicker(options, response => {
console.log('Response = ', response);
if (response.didCancel) {
console.log('User cancelled image picker');
} else if (response.error) {
console.log('ImagePicker Error: ', response.error);
} else if (response.customButton) {
console.log('User tapped custom button: ', response.customButton);
} else {
let source = { uri: response.uri };
// You can also display the image using data:
// let source = { uri: 'data:image/jpeg;base64,' + response.data };
this.setState({
logo: source
});
}
});
Remember that you also should pass a name key too, like below:
let url = "",
headers = "",
method = "POST",
body = new FormData(),
uri = "URI of the picked image.";
body.append("photo", {
name: "Just a name",
uri : Platform.OS === "android" ? uri : uri.replace("file://", "")
}
);
fetch(url, method, headers, body)
.then(function (response) {
})
.catch(function (error) {
});
function uploadProfilePicture(mImage) {
var data = new FormData();
data.append('theFile', { uri: mImage.uri, name: 'profile_photo.jpg', type: 'image/jpg' });
fetch(AppConstant.BASE_URL + AppConstant.upload_media, {
method: 'POST',
body: data
})
.then((response) => response.json())
.then((responseJson) => {
var err = 'error_message' in responseJson ? true : false
if (err) {
alert(responseJson.error_message)
} else {
alert(JSON.stringify(responseJson))
}
})
.catch((error) => {
console.error(error);
alert(error)
});
}
If anyone has issues with using fetch in iOS, check out react-native-file-upload I have found it to be extremely helpful in both image uploading and regular posts.