How to save pdf from an api response to file system in react native? - react-native

I'm receieving a blob from api and i want to save it as a pdf document to file system.But on saving I'm getting a file with size 0B in my mobile
Code
export const getParkingReciept = (bookindId) => {
return async function (dispatch, getState) {
try {
const TOKEN = getState().Auth.token;
const formdata = new FormData();
formdata.append("booking_id", bookindId);
RNFetchBlob.fetch(
'POST',
`${BASE_URL}parking-space/booking/receipt`,
{
'Accept': 'application/json',
'Authorization': `Bearer ${TOKEN}`,
'Content-Type': 'multipart/form-data'
},[
{ name : 'booking_id', data: bookindId.toString()}
]
)
.then(
response => {
console.log("response is ",response);
response.blob().then(res=>console.log(checkPermission(res,response.taskId)));
console.log("pdf base64 is ", response.base64());
}
).catch((error) => {
// error handling
console.log("Error", error)
}
);
}catch (e) {
if (e.response) {
console.log("error response is ", e.response);
} else if (e.request) {
console.log(e.request);
} else {
console.log('Error', e);
}
console.log(e.config);
}
}
const checkPermission=async (file,name) => {
try {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE,
{
title: "Cool Photo App Camera Permission",
message:
"Cool Photo App needs access to your camera " +
"so you can take awesome pictures.",
buttonNeutral: "Ask Me Later",
buttonNegative: "Cancel",
buttonPositive: "OK"
}
);
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
console.log("You can write to external storage");
var path = RNFS.DownloadDirectoryPath + '/'+name+".pdf";
console.log("pdf being written is ",file);
RNFS.writeFile(path, file, 'utf8')
.then((success) => {
console.log('FILE WRITTEN!');
RNFetchBlob.fs.scanFile([ { path : path, mime : "application/pdf" } ])
// .then(() => {
// console.log("scan file success")
// })
// .catch((err) => {
// console.log("scan file error")
// })
})
.catch((err) => {
console.log(err.message);
});
} else {
console.log("permission denied");
}
} catch (err) {
console.warn(err);
}
};
reponse I get from fetch is
on calling blob() function of response what I get is
There is type Application/pdf there ,but in base 64 string does not start with JVBERi it starts with some SFRUU,Is that a valid pdf file?. What am I missing ? what am I doing wrong here?

This answer solves your problem , gives you detailed explanation about how to download files from a network request using rn fetch blob
https://stackoverflow.com/a/56890611/7324484
Once you downloaded the file or you can open the pdf directly using
https://www.npmjs.com/package/react-native-pdf

Related

Issue in uploading files with react native with axios

I am trying to upload files to server in react-native application with axios as http handler.
My code stands as:
let promises = [];
const body = new FormData();
console.log(body);
body.append('device_name', this.state.deviceInfo.DeviceName);
body.append('device_id', this.state.deviceInfo.DeviceID);
body.append('device_ip', this.state.deviceInfo.DeviceIP);
body.append('device_os', this.state.deviceInfo.DeviceOS);
body.append('upload_type', 'user');
body.append('user_name', user.Email);
body.append('file1', {
uri: this.state.newImageUrl.uri,
name: 'test.jpg',
type: 'image/jpg',
});
promises.push(
apiUploadDocs(body)
.then(res => {
profileImageName = res[0].NewFileName;
})
.catch(err => {
console.log('this error', err);
}),
);
My apiUploadDocs is as :
export const apiUploadDocs = body => {
return new Promise((resolve, reject) => {
axios
.post(ApiRoutes.uploadDocs, body,{headers:{'content-Type': `multipart/form-data`}})
.then(res => {
console.log('upload success');
console.log(res);
})
.catch(err => {
console.log('upload error', err);
if (err.response) {
}
reject(Constant.network.networkError);
});
});
};
Every assigned variable has correct values upon logging and the api is working good when I try to upload from Postman.
But this snippet here results in an error which is undefined when logged.
I have tried trimming the 'file://' from the uri, as suggested by some answers here in stackoverflow.
I cant figure it out. Can you help me finding whats wrong here??
PS: The body when logged is:
{
"_parts":[
[
"device_name",
"sdk_gphone_x86"
],
[
"device_id",
"xxxxxxxxxxxxx"
],
[
"device_ip",
"10.0.2.xx"
],
[
"device_os",
"goldfish_x86"
],
[
"upload_type",
"user"
],
[
"user_name",
"xxxxx#gmail.com"
],
[
"file1",
[
"Object"
]
]
]
}
if it is of any reference.
I've found a link to uploading image in react-native.
https://aboutreact.com/file-uploading-in-react-native/
This might be of some help to you.
let uploadImage = async () => {
//Check if any file is selected or not
if (singleFile != null) {
//If file selected then create FormData
const fileToUpload = singleFile;
const data = new FormData();
data.append('name', 'Image Upload');
data.append('file_attachment', fileToUpload);
let res = await fetch(
'http://localhost//webservice/user/uploadImage',
{
method: 'post',
body: data,
headers: {
'Content-Type': 'multipart/form-data; ',
},
}
);
let responseJson = await res.json();
if (responseJson.status == 1) {
alert('Upload Successful');
}
} else {
//if no file selected the show alert
alert('Please Select File first');
}
};

Display profile picture with expo

I am new to React native and expo and I'm trying to handle the user's profile (edit the profile, profile picture, etc...).
I managed to successfully retrieve the user's info from the API and send the changes like the username or password to it but I can't do it for the profile picture.
I've already tried with axios (what I use to communicate to the API):
async componentDidMount() {
const token = await this.getToken();
const AuthStr = 'Bearer '.concat(token);
await axios.get('https://....jpg', { headers: { Authorization: AuthStr } })
.then(res => {
console.log('RESPONSE: ', res.data);
})
.catch((error) => {
console.log('error ' + error);
});
}
But it returns me error 500
Then I tried with the FileSystem from Expo:
let download = FileSystem.createDownloadResumable('https://...jpg',
FileSystem.documentDirectory + 'profile.jpg',
{ headers: { Authorization: AuthStr } },
null,
null);
try {
const { uri } = await download.downloadAsync();
console.log('Finished downloading to ', uri);
this.setState({
pic: uri,
});
} catch (e) {
console.error(e);
}
let info = await FileSystem.getInfoAsync(this.state.pic, null);
console.log(info);
The log says the file is downloaded and the path is something like this:
file:///var/mobile/Containers/Data/Application/20CF6F63-E14D-4C14-9078-EEAF50A37DE1/Documents/ExponentExperienceData/%2540anonymous%app/profile.jpg
The getInfoAsync() returns true, meanings that the image exists in the filesystem. When I try to render it with
<Image source={{uri: this.state.pic}}/>
It displays nothing. What do I do wrong ?
the temporary solution is to move the image to the gallery
let { uri } = await FileSystem.downloadAsync(this.props.item.imgUrl, FileSystem.cacheDirectory + ${this.props.item}.jpg);
const { status } = await Permissions.askAsync(Permissions.CAMERA_ROLL);
if (status === 'granted') {
CameraRoll.saveToCameraRoll(uri).then((uriGallery) => {
//here you have the url of the gallery to be able to use it
});
}
This is the link I referred to in writing the answer.

Firebase Cloud Functions Call : error : Object message : "Bad Request" status : "INVALID_ARGUMENT"

first of all i am working with react-native
i wanted to use Custom Claims on my project since it seems to fit the role distribution i expect to use on my app.
after setting my app following the documentation i succeed on creating some functions
but, here's the thing, when i try to call a function by fetching the endpoint i always get this error :
in the console
error
:
Object
message
:
"Bad Request"
status
:
"INVALID_ARGUMENT"
in firebase console
addAdminRoleTest Request body is missing data. { email: 'dev#test.com' }
i couldn't find any answer to that except that i send wrong information from my fetch but i don't understand why.
i even tried to simplify my function only to get the data i sent but i had the exact same error
find below my cloud function & the calling method :
functions/index.js
exports.addAdminRole = functions.https.onCall((data, context) => {
// get user
return admin.auth().getUserByEmail(data.email).then(user => {
// if not already (admin)
if(user.customClaims && (user.customClaims).admin === true) {
return;
}
// add custom claim (admin)
return admin.auth().setCustomUserClaims(user.uid, {
admin: true
});
}).then(() => {
return {
message: `Bravo : ${data.email} fait partie de l'équipe Admins`
}
}).catch(err => {
return err;
});
});
simplified function :
exports.addAdminRoleTest = functions.https.onCall(data => {
console.log("parse data : "+JSON.parse(data));
return (
JSON.parse(data)
);
});
adminScreen.js
function httpAddAdminRole() {
const initRequest = {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
},
body:JSON.stringify({
email: 'dev#test.com'
})
}
console.log(initRequest);
return fetch('https://us-central1-my-project.cloudfunctions.net/addAdminRole', initRequest)
.catch(err => console.log(err))
.then(res => res.json())
.then(parsedRes => {
console.log(parsedRes);
});
}
in the end this was mostly json knowledge that missed me
my body should have data included
here's the answer i came to :
functions/index.js
exports.addAdminRole = functions.https.onCall((data, context) => {
const dataParsed = JSON.parse(data);
// get user
return admin.auth().getUserByEmail(dataParsed.email).then(user => {
// if not already (admin)
if(user.customClaims && (user.customClaims).admin === true) {
console.log(dataParsed.email + " is already an Admin");
return;
}
// add custom claim (admin)
return admin.auth().setCustomUserClaims(user.uid, {
admin: true
});
}).then(() => {
return {
message: `Bravo : ${dataParsed.email} is now an Admin`
}
}).catch(err => {
return err;
});
});
adminScreen.js
function httpAddAdminRole(mail) {
const initRequest = {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
},
body:JSON.stringify({
data:JSON.stringify({
email: mail
})
})
}
console.log(initRequest);
return fetch('https://us-central1-my-project.cloudfunctions.net/addAdminRole', initRequest)
.catch(err => console.log(err))
.then(res => res.json())
.then(parsedRes => {
console.log(parsedRes);
});
}

AsyncStorage data changing upon app restart

I'm currently calling a JSON api to set an auth token which I'll just be storing in the AsyncStorage to persist between app life so a user doesn't have to log in every single time.
I'm currently setting that token like so:
fetch(url, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify(this.state)
})
.then( resp => {
return resp.json();
})
.then( async (data) => {
if ('error' in data) {
this.setState({
error: data.error,
password: ''
})
this.secondTextInput.focus();
}
if ('access_token' in data) {
try {
await AsyncStorage.setItem('access_token', data.access_token);
} catch (error) {
return error;
}
this.props.navigation.navigate('Main');
}
})
.catch(
error => {
console.error(error)
return error;
}
);
If I then call AsyncStorage.getItem('access_token') After killing the app or reloading it I'm winding up with this output:
{
"_40":0,
"_65":0,
"_55":null,
"_72":null
}
If I then call AsyncStorage.getItem('access_token') Before killing the app or reloading it I'm winding up with the correct access token. I've double checked the code and I'm not using AsyncStorage.setItem('access_token') anywhere else.
This is how I'm retrieving my token:
componentDidMount() {
console.warn('Mounting');
try {
let token = AsyncStorage.getItem('access_token');
console.warn(token);
if(token !== null) {
console.error(token);
}
} catch (error) {}
AsyncStorage.getItem() is a asynchronous action just like setItem(), so you need to wait until the Promise has been resolved before logging.
Edit
Tip: if you see some strange output like that it is always related to a Promise which is not yet resolved or rejected
I've solved my issue by using #dentemm's recommendation of creating an async function.
async _getToken() {
try {
var token = await AsyncStorage.getItem('access_token');
return token;
} catch(e) {
console.error(e);
}
}
componentDidMount() {
let token = null;
this._getToken()
.then( rsp => {
fetch(global.url + '/api/auth/refresh', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + rsp
}
})
.then(rsp => {
return rsp.json();
})
.then(data => {
if('access_token' in data) {
try {
AsyncStorage.setItem('access_token', data.access_token);
} catch (error) {
return error;
}
this.props.navigation.navigate('Main');
}
})
.catch( error => {
return error;
})
});
}
This way I can get my token from the storage then run my refresh function to get an updated token to use for future requests.

Losing connection during a fetch causes crash in React-Native app

If the internet connection is lost during a fetch in my react-native app I get Network request failed and the app crashes.
updateClientData() {
var cachedData = null;
AsyncStorage.getItem('cachedData').then((cachedDataString) => {
cachedData = JSON.parse(cachedDataString);
})
.done(() => {
if (cachedData) {
const base64 = require('base-64');
return fetch('https://...data.json', {
method: 'get',
headers: {
'Authorization': 'Basic '+base64.encode("..."),
}
})
.then( (response) => {
// never called:
return response.json();
})
.catch( (error) => {
//Shouldn't this catch network errors? It never gets called.
console.log('caught network error');
})
.then( (responseJSON) => {
//do something with the JSON
})
}
});
},
I would love to be able to handle this gracefully rather than have it crash. Any ideas?
For some reason, moving the AsyncStorage call out of this function made it work fine. I didn't actually need it until I had the result of the fetch anyway, so I moved it.
This works now:
updateClientData() {
const base64 = require('base-64');
return fetch(clientListURL, {
method: 'get',
headers: {
'Authorization': 'Basic '+base64.encode("..."),
}
})
.then( (response) => {
return response.json();
})
.catch( (error) => {
console.log('error...')
})
.then( (responseJSON) => {
// now do something with the JSON and the data from Async Storage
}
},