react-native-fs cannot access storage - react-native

i'm using react-native-fs package to save files to android storage.
when i perform these two lines of code, it works
await RNFS.readDir(RNFS.DocumentDirectoryPath);
=====>/data/user/0/com.youtubedljs/files
await RNFS.readDir(RNFS.CachesDirectoryPath);
=====>/data/user/0/com.youtubedljs/cache
but when i try to access external storage, like Download or documents files
await RNFS.readDir(RNFS.DownloadDirectoryPath);
i get this error
Error: Attempt to get length of null array
i already granted storage permissions to the application, it didn't work.
Update: Works fine on android 8.1, i think that the package doesn't support android 10 yet

Need to give read-write permissions.
that's okay.. but you also need to get incode permissions, before accessing file
import {PermissionsAndroid} from 'react-native';
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE,
);
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
//alert('You can use the location');
console.log('write granted');
return true;
} else {
return false;
}
} catch (err) {
console.warn(err);
return false;
}

First you have to ask for permission
then you can save the into download folder of mobile
import {request, PERMISSIONS} from 'react-native-permissions';
request(
Platform.OS === 'ios'
? PERMISSIONS.IOS.MEDIA_LIBRARY
: PERMISSIONS.ANDROID.WRITE_EXTERNAL_STORAGE,
).then(result => {
console.log(result);
);
});
var path = RNFS.DownloadDirectoryPath + '/filename.txt';

Related

How to create a .pdf or .csv file automatically in React Native for Android 11 (or later)

The following code works up to Android 10, it is creating a csv file in the DCIM folder:
import * as FileSystem from 'expo-file-system';
import * as MediaLibrary from 'expo-media-library';
export async function saveCSV() {
const permission = await MediaLibrary.requestPermissionsAsync();
if (permission.status != 'granted') {
console.log("Permission not Granted!")
return;
}
// CSVLocation
const directoryUri = FileSystem.documentDirectory;
const fileUri = directoryUri + `formData.csv`;
// Save to DCIM folder
const asset = await MediaLibrary.createAssetAsync(fileUri);
try {
const album = await MediaLibrary.getAlbumAsync('album');
if (album == null) {
console.log("ASSET", asset)
await MediaLibrary.createAlbumAsync('album', asset, true);
} else {
await MediaLibrary.addAssetsToAlbumAsync([asset], album, true)
.then(() => {
console.log('File Saved Successfully!');
})
.catch((err: string) => {
console.log('Error In Saving File!', err);
});
}
} catch (e) {
console.log(e);
}
}
Previously this line of code was executed in another function to create a file in the fileUri used above:
await FileSystem.writeAsStringAsync(fileUri, CSVheader + newInfo);
This issue has been described here: https://github.com/expo/expo/issues/12060
In short: Expo Media library is able to save image/video/audio assets so it will fail with other file types. Weirdly enough it was working fine with .pdf and .csv up to Android 10.
In the link above, and also on stackoverflow there are solutions using StorageAccessFramework. However, the user needs to create a subdirectory inside Downloads every time a file needs to be saved. I would like to make it automatically without any popups (after permission is granted).
The destination folder doesn't matter as long as it is accessible by the user later.

Why app is not asking for requested location permission?

What im trying to do is to ask the user for location permission, and if the user doesn't allow the permission, cannot navigate to other screens.
I have used expo-location for this, and when I test the app locally, it asks me to allow or not allow the permission for the location, but when I generate an APK, to test it on android, it shows me the alert that I dont have the location permission, and when I go to the app setting to allow it manually, it says No permission requested
function requestLocationPermission() {
return Location.requestForegroundPermissionsAsync().then((handleResponse) => {
if (handleResponse.status === 'granted') {
setLocationPermission(handleResponse.status);
} else if (handleResponse.status !== 'granted') {
alert('Map/Gps needs the appropriate permissions.');
return Location.requestForegroundPermissionsAsync();
}
});
}
useEffect(() => {
requestLocationPermission();
buttonText();
}, [locationPermission]);
const confirm = () => {
if (locationPermission === "granted") {
navigation.navigate('CreateProfile',
} else {
requestLocationPermission();
}
};
So, in the APK it returns me: alert('Map/Gps needs the appropriate permissions.');
How can I fix this?

Permission already granted but keep telling not granted REACT-NATIVE

im building a music player and I need to acces to the music files, so the first thing is ask for permissions to acces to the files:
componentDidMount() {
this.requestPermission();
}
requestPermission = async () => {
try {
const userResponse = await PermissionsAndroid.requestMultiple([
PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE,
PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE,
]);
if (userResponse === PermissionsAndroid.RESULTS.GRANTED) {
alert("nice");
}else {
alert("no nice");
}
}catch (error) {
alert(error)
}
};
even when I already clicked in allow, and reload the project it wil say no nice, i open the config of the app and the permission to storage is fine, so dont why is this happening
According to documentation requestMultiple will return an object with the permissions as keys and strings as values (see result strings above) indicating whether the user allowed or denied the request or does not want to be asked again.
try this
if (userResponse[PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE] === PermissionsAndroid.RESULTS.GRANTED && userResponse[PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE] ===
PermissionsAndroid.RESULTS.GRANTED
) {
alert('nice');
} else {
alert('no nice');
}

How do I request multiple permissions at once in react native

I'd like to request permissions on one page instead of waiting for each particular situation. However, I do not want multiple popups. Is there a way to ask for permissions with a single popup/modal.
On the android side I found this post and this, which look promising, but I have yet to find something for iOS.
In Android
First add permissions in to the AndroidManifest.xml file and then
if (Platform.OS === 'android') {
PermissionsAndroid.requestMultiple(
[PermissionsAndroid.PERMISSIONS.CAMERA,
PermissionsAndroid.PERMISSIONS.READ_CONTACTS,
PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
PermissionsAndroid.PERMISSIONS.ACCESS_COARSE_LOCATION,
PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE,
PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE]
).then((result) => {
if (result['android.permission.ACCESS_COARSE_LOCATION']
&& result['android.permission.CAMERA']
&& result['android.permission.READ_CONTACTS']
&& result['android.permission.ACCESS_FINE_LOCATION']
&& result['android.permission.READ_EXTERNAL_STORAGE']
&& result['android.permission.WRITE_EXTERNAL_STORAGE'] === 'granted') {
this.setState({
permissionsGranted: true
});
} else if (result['android.permission.ACCESS_COARSE_LOCATION']
|| result['android.permission.CAMERA']
|| result['android.permission.READ_CONTACTS']
|| result['android.permission.ACCESS_FINE_LOCATION']
|| result['android.permission.READ_EXTERNAL_STORAGE']
|| result['android.permission.WRITE_EXTERNAL_STORAGE'] === 'never_ask_again') {
this.refs.toast.show('Please Go into Settings -> Applications -> APP_NAME -> Permissions and Allow permissions to continue');
}
});
}
In iOS
In the info section of your project on XCode
Add the permissions and the description
say - ex: Privacy - Contacts Usage Description
then,
Permissions.request('photo').then(response => {
if (response === 'authorized') {
iPhotoPermission = true;
}
Permissions.request('contact').then(response => {
if (response === 'authorized') {
iPhotoPermission = true;
}
});
});
Makes sure you also add respective permissions in manifest file as well.
export async function GetAllPermissions() {
try {
if (Platform.OS === "android") {
const userResponse = await PermissionsAndroid.requestMultiple([
PermissionsAndroid.PERMISSIONS.READ_CONTACTS,
PermissionsAndroid.PERMISSIONS.CALL_PHONE
]);
return userResponse;
}
} catch (err) {
Warning(err);
}
return null;
}
To request multiple permissions I will suggest you to use npm module as its saves time and easy to setup and most important you don't have to worry about the platforms :)
Installation
npm install --save react-native-permissions
Usage
import Permissions from 'react-native-permissions'
// Check the status of multiple permissions
_checkCameraAndPhotos = () => {
Permissions.checkMultiple(['camera', 'photo']).then(response => {
//response is an object mapping type to permission
this.setState({
cameraPermission: response.camera,
photoPermission: response.photo,
})
})
}
Don't forget to add permissions to AndroidManifest.xml for android and Info.plist for iOS (Xcode >= 8).
First add your permissions to "android/app/src/main/AndroidManifest.xml" file. You can use npm module in order to achieve your goal.
npm install --save react-native-permissions
After installation, you can use checkMultiple function to ask multiple permissions. Following code illustrates how to ask permission for ANDROID.CAMERA and ANDROID.READ_EXTERNAL_STORAGE :
checkMultiple([
PERMISSIONS.ANDROID.CAMERA,
PERMISSIONS.ANDROID.READ_EXTERNAL_STORAGE,
]).then(result => {
console.log(result);
});
The result will be an object like this
{"android.permission.CAMERA": "denied", "android.permission.READ_EXTERNAL_STORAGE": "denied"}
To handle this result, you can use this basic code example
checkPermissions = () => {
if (Platform.OS !== 'android') {
return;
}
checkMultiple([
PERMISSIONS.ANDROID.CAMERA,
PERMISSIONS.ANDROID.READ_EXTERNAL_STORAGE,
]).then(result => {
if (
!result ||
!result[PERMISSIONS.ANDROID.CAMERA] ||
!result[PERMISSIONS.ANDROID.READ_EXTERNAL_STORAGE]
) {
console.log('could not get any result. Please try later.');
}
if (
result[PERMISSIONS.ANDROID.CAMERA] === RESULTS.GRANTED &&
result[PERMISSIONS.ANDROID.READ_EXTERNAL_STORAGE] === RESULTS.GRANTED
) {
console.log('granted for both permissions');
// do smthing here
}
});
};
There is also a way to request multiple permissions at once using react-native-permissions package.
you can also implement your own conditions according to need, i am just writing a simple solution
requestMultiple will not ask again if permissions are already granted.
import { checkMultiple, requestMultiple, PERMISSIONS } from 'react-native-permissions';
const verifyPermissions = async () => {
let perm = [ PERMISSIONS.IOS.CAMERA, PERMISSIONS.IOS.PHOTO_LIBRARY ];
if (Platform.OS == 'android') {
perm = [ PERMISSIONS.ANDROID.CAMERA, PERMISSIONS.ANDROID.READ_EXTERNAL_STORAGE ];
}
let permissionStatuses = await requestMultiple(perm);
console.log('obj', permissionStatuses);
console.log('Camera', permissionStatuses[perm[0]]);
console.log('photo', permissionStatuses[[ perm[1] ]]);
const result = permissionStatuses[perm[0]];
if (result !== 'granted') {
Alert.alert('Insufficient permissions!', 'You need to grant camera and library access permissions to use this app.', [
{ text: 'Okay' }
]);
return false;
}
return true;
};

Preload / Pre-populate PouchDB in React Native Application

Is it possible to preload / pre-populate a database in my React Native application and then the first time it is run, simply do a sync? I already have most, if not all of the database information before the app is distributed, it would be awesome if it just had to do a quick sync when the app is run. Any ideas how I would go about doing that?
I found this - https://pouchdb.com/2016/04/28/prebuilt-databases-with-pouchdb.html but it doesn't mention React Native
Using:
pouchdb-find: ^7.0.0
pouchdb-react-native: ^6.4.1
react: 16.3.1
react-moment: ^0.7.9
react-native: ~0.55.2
Thanks for any pointers.
Update Here is the code I'm using to try the loading of a dump file. This code exists in /screens/Home.js
The dump file is located in /client/dbfile/database.txt
var db = new PouchDB("cs1");
db.get("_local/initial_load_complete")
.catch(function(err) {
console.log("loading dumpfile");
if (err.status !== 404) {
// 404 means not found
throw err;
}
db.load("/client/dbfile/database.txt").then(function() {
return db.put({ _id: "_local/initial_load_complete" });
});
})
.then(function() {
// at this point, we are sure that
// initial replication is complete
console.log("loading is complete!");
return db.allDocs({ include_docs: true });
})
.then(
function(res) {
// display all docs for debugging purposes (empty)
console.log(res);
});
this.localDB = db;
When this runs my console displays this - showing there have been 0 rows added.
Object {
"offset": 0,
"rows": Array [],
"total_rows": 0,
}
Possible Unhandled Promise Rejection (id: 0):
Object {
"message": undefined,
"name": "unknown",
"status": 0,
}
In my project I have couple of db docs I distribute with app (translations JSON is the one good example).
So at app init I just try to read translations doc from db, if there is none - I import content from js module and store in db.
Then translations changes just being replicated from server to local db.
//transmob.js
const transMobFile = {
//content goes here
);
module.exports = transMobFile
//dbInit.js
import transMobFile from 'data/transMob';
..
PDB.getDoc('TransMob')
.then((doc)=> {
if (!doc) {
global.locales = transMobFile.localesMob; // locales
global.translations = transMobFile.langMob; // translations
return PDB.saveDoc('TransMob', transMobFile)
}
})
You can use react-native-fs to load a file from /android/app/src/main/assets. Just put the file into the assets folder and read it with RNFS.readFileAssets.
import PouchDB from 'pouchdb-core';
PouchDB
.plugin(require('pouchdb-find'))
.plugin(require('pouchdb-load'));
import RNFS from 'react-native-fs';
const localDB = new PouchDB("cs1", {adapter: 'asyncstorage'});
localDB.get("_local/initial_load_complete")
.catch(function(err) {
console.log("loading dumpfile");
if (err.status !== 404) {
// 404 means not found
throw err;
}
RNFS.readFileAssets('yourdb.txt', 'utf8')
.then((contents) => {
localDB.load(contents).then(function() {
return localDB.put({ _id: "_local/initial_load_complete" });
}).then(function() {
// at this point, we are sure that
// initial replication is complete
console.log("loading is complete!");
return localDB.allDocs({ include_docs: true }).then(
function(res) {
// display all docs for debugging purposes (empty)
console.log(res);
});
}, function(err) {
console.log(err);
});
})
})
You'll need to rebuild your project, reloading is not sufficient.
My project crashes when I attempt to load a 30MB file, so I probably will split it into a few smaller files. Check out https://github.com/pouchdb-community/pouchdb-load to see how this works if needed.
I found that the db.load() function from the pouchdb-load module requires a URL. I was pointing it to a file path on the device's filesystem. I placed my database.txt file on my server, changed it to use the url and it worked.
In my mind this isn't ideal because if they install the app and have slow wireless, it still has to pull the file from the server. It is still much faster than performing a full-on replicate when the app opens for the first time however.