How to get Absolute path of a file in react-native? - react-native

I am looking for a file picker in react-native which returns me Absolute Path of the file picked. I am currently using react-native-document-picker, but it gives me the relative path in the format of content://com.android.providers.media.documents/document/....... As I want to compress my video file, libraries like react-native-ffmpeg and react-native-video-processing require Absolute path of a file.

I actually figured this out myself. You can get Absolute path in 3 ways.
The most convenient way : Use react-native-document-picker, on selection it will give you a Relative path, something like this content://com.android....... Pass that Relative path to Stat(filepath) function of the react-native-fetch-blob library. The object will return absolute path. Append the path with file:// to use it for further operations.
The other 2 ways are by using react-native-image picker and CameraRoll (React Native Library)
I hope this helps !
Edit:
Please make sure you run the app on hardware device rather than Virtual Device to test it.

Install react-native-fetch-blob to get the path of the file.
Below is an example.
pickFile = async () => {
try {
const res = await DocumentPicker.pick({
type: [DocumentPicker.types.allFiles],
});
console.log(res.uri);
//output: content://com.android.providers.media.documents/document/image%3A4055
RNFetchBlob.fs
.stat(res.uri)
.then((stats) => {
console.log(stats.path);
//output: /storage/emulated/0/WhatsApp/Media/WhatsApp Images/IMG-20200831-WA0019.jpg
})
.catch((err) => {
console.log(err);
});
} catch (err) {
if (DocumentPicker.isCancel(err)) {
} else {
throw err;
}
}};

First you have to ask for Android Permissions so make sure you call this function first:
export const requestReadExternalStorage = () => {
PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE);
};
After you call this function and Permissions are accepted you can pass the uri to this function:
export const getPath = (uri: string) => {
if (uri.startsWith('content://')) {
return RNFetchBlob.fs.stat(uri).then(info => info?.path);
}
return uri;
};
Then you just need to call it and use the real uri now, like this:
// res?.uri is the uri returned from the DocumentPicker.pick() response.
const uri = await getPath(res?.uri);

You may forget to request proper permissions for that like so (andriod only):
export async function requestStoragePermission() {
if (Platform.OS !== "android") return true
const pm1 = await PermissionsAndroid.check(PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE);
const pm2 = await PermissionsAndroid.check(PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE);
if (pm1 && pm2) return true
const userResponse = await PermissionsAndroid.requestMultiple([
PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE,
PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE
]);
if (userResponse['android.permission.READ_EXTERNAL_STORAGE'] === 'granted' &&
userResponse['android.permission.WRITE_EXTERNAL_STORAGE'] === 'granted') {
return true
} else {
return false
}
}

Try this, maybe it will help you https://www.npmjs.com/package/react-native-file-share-for-android
But its support only for Android
const uploadDocunment = async finalSubmit => {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE,
{
title: 'Storage Permission',
message: 'App needs access to memory to download the file ',
},
);
if (granted != PermissionsAndroid.RESULTS.GRANTED) {
ToastAndroid.showWithGravity(
'You need to give storage permission to download the file',
ToastAndroid.SHORT,
ToastAndroid.BOTTOM,
);
return false;
}
try {
DocumentPicker.pick({
type: [DocumentPicker.types.plainText],
}).then(res => {
RNFetchBlob.fs.readFile(res.uri, 'utf8').then(text1 => {
ToastAndroid.showWithGravity(
'Docunment is Ready!',
ToastAndroid.SHORT,
ToastAndroid.BOTTOM,
);
});
});
} catch (err) {
if (DocumentPicker.isCancel(err)) {
ToastAndroid.showWithGravity(
'File not Selected',
ToastAndroid.SHORT,
ToastAndroid.BOTTOM,
);
} else {
throw err;
}
}
};
uploadDocunment();
};

Related

Expo - Access to all photos is required to do this operation

I have an application built with expo and when a user download a picture on iOS, I'm getting this error on bugsnag:
Access to all photos is required to do this operation.
I'm not myself an iPhone user. I could replicate this with someone else iPhone, got the error but nothing apparent on the iPhone's user.
I realized later that I forgot to add this to app.config.json
export default {
...
"plugins": [
[
"expo-media-library",
{
"photosPermission": "Allow $(PRODUCT_NAME) to access your photos.",
"savePhotosPermission": "Allow $(PRODUCT_NAME) to save photos."
}
]
]
}
After adding it, it seems to have fixed the issue on the testflight (probably because the user had to uninstall-reinstall the app).
However, after deploying the app in the store, the error keeps happening. I suppose it should have re-asking for permissions ? (If so, I have no idea how)
Here's how I register the permissions and download the picture
import * as MediaLibrary from 'expo-media-library';
import * as FileSystem from 'expo-file-system';
import logger from '~application/utility/logger';
const albumName = "MyApplicationName";
const download = async (uri, fileName, headers, callback) => {
registerLibraryPermissions()
.then((granted) => granted ? downloadFile(uri, fileName, headers, callback) : null)
};
const downloadFile = async (uri, fileName, headers, callback) => {
if (Object.keys(headers).length === 0) return null;
try {
return FileSystem.createDownloadResumable(uri, FileSystem.documentDirectory + fileName, {headers: headers}, callback)
.downloadAsync()
.then(({uri}) => moveToGallery(uri))
.catch(error => logger.log("Error downloading the file", error));
} catch (error) {
logger.log("Error while downloading the photo", error)
}
}
const moveToGallery = async (localUri) => {
try {
const asset = await MediaLibrary.createAssetAsync(localUri);
return await MediaLibrary.getAlbumAsync(albumName).then(album => {
if (album !== null) {
return MediaLibrary.addAssetsToAlbumAsync([asset], album, false)
.catch((error) => logger.log('Error while adding photo', error));
}
return MediaLibrary.createAlbumAsync(albumName, asset, false)
.catch((error) => logger.log('Error while saving photo', error));
});
} catch (error) {
logger.log('Error while saving photo', error)
}
}
const registerLibraryPermissions = async () => {
try {
const {status: existingStatus} = await MediaLibrary.getPermissionsAsync();
let finalStatus = existingStatus;
if (existingStatus !== 'granted') {
const {status} = await MediaLibrary.requestPermissionsAsync();
finalStatus = status;
}
if (finalStatus !== 'granted') {
alert("L'accès à la média libairie n'a pas pu être activé sur votre téléphone");
return false;
}
return true;
} catch (error) {
logger.log('Error getting the permissions for media library', error);
}
};
export default {
download,
};
Am I missing something? Thanks

getdownloadurl from Firebase storage expo cli

I am trying to display the image from firebase storage. Below is the file location copied from firebase storage. It is a jpeg file
profile/2186uPKjgo4pMOQNm0Cm/profilepic
My following code returned error.
useEffect(() => {
function geturl(){
const filename = "profile/"+userid+"/profilepic.jpeg";
var ref = firebase.storage().ref(filename);
console.log(filename);
// This returns the exact file name
ref.getDownloadURL().then((url)=> {
console.log(url);
});
}
geturl();
}, []);
I got this error [object Object]. After that, I tried the following code async await
useEffect(() => {
async function geturl(){
const filename = "profile/"+userid+"/profilepic.jpeg";
var ref = firebase.storage().ref(filename);
console.log("inside geturl");
const downloadurl = await ref.getDownloadURL();
console.log(downloadurl);
}
geturl();
}, []);
Now Im getting the following error.
Possible Unhandled Promise Rejection (id: 29):
"code_": "storage/object-not-found",
"message_": "Firebase Storage: Object 'profile/2186uPKjgo4pMOQNm0Cm/profilepic.jpeg' does not exist.",
"name_": "FirebaseError",
"serverResponse_": "{
\"error\": {
\"code\": 404,
\"message\": \"Not Found. Could not get object\",
\"status\": \"GET_OBJECT\"
}
}",
}
Please let me know how I can get the url?
here you go you can use this function it uploads image to firebase storage and get the image uri at the same time
const uploadImage = async () => {
const response = await fetch(image);
const blob = await response.blob();
let filename = image.substring(image.lastIndexOf('/')+1);
const ext = filename.split('.').pop();
const name = filename.split('.').slice(0, -1).join('.');
filename = name + Date.now() + '.' + ext;
try {
var ref = firebase.storage().ref().child('post-images/'+filename);
await ref.put(blob)
.then(snapshot => {
return snapshot.ref.getDownloadURL();
})
.then(downloadURL => {
console.log(`Successfully uploaded file and got download link');
return downloadURL;
});
return null;
} catch (error) {
return null;
}
}

reaact-native get data from file from uri

This is a homework type question, please help as I'm new on react-native.
In react-native, I'm using react-native-document-picker, from the code in readme:
selectFiles = () => {
try {
DocumentPicker.pickMultiple({
type: [DocumentPicker.types.allFiles],
}).then((results) => {
console.log(result[0].uri);
}
});
} catch (err) { }
};
It provides me with an URI, how can convert that into path and read data of that file ?
Here is my solution :
var RNFS = require('react-native-fs');
...
RNFS.readFile(results[0].uri)
.then((file) => {
that.setState({code: file});
})
.catch((error) => console.log('err: ' + error));

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.

how to implement cache in react-native-video

How do we implement caching in react-native-video? Basically, when a video is currently streaming from a network resource, how do we save the video somewhere, and then retrieve it when the same resource is access. What is the best approach for this?
The best approach that i would refer you is using react-native-fetch-blob, you can implement it like this:
const RNFetchBlob = require('react-native-fetch-blob').default;
const {
fs
} = RNFetchBlob;
const baseCacheDir = fs.dirs.CacheDir + '/videocache';
//call the downloadVideo function
downloadVideo('http://....',baseCacheDir)
//Function to download a file..
const activeDownloads = {};
function downloadVideo(fromUrl, toFile) {
// use toFile as the key
activeDownloads[toFile] = new Promise((resolve, reject) => {
RNFetchBlob
.config({path: toFile})
.fetch('GET', fromUrl)
.then(res => {
if (Math.floor(res.respInfo.status / 100) !== 2) {
throw new Error('Failed to successfully download video');
}
resolve(toFile);
})
.catch(err => {
return deleteFile(toFile)
.then(() => reject(err));
})
.finally(() => {
// cleanup
delete activeDownloads[toFile];
});
});
return activeDownloads[toFile];
}
//To delete a file..
function deleteFile(filePath) {
return fs.stat(filePath)
.then(res => res && res.type === 'file')
.then(exists => exists && fs.unlink(filePath)) //if file exist
.catch((err) => {
// swallow error to always resolve
});
}
Cheers:)