我的应用程序需要在外部存储中的特殊文件夹中保存一个JSON文件。我试图以这种方式使用RNFS (https://github.com/itinance/react-native-fs)来实现它:
const saveData = async () => {
var path = `${RNFS.ExternalStorageDirectoryPath}/MyApp`;
RNFS.mkdir(path);
path += '/data.json';
RNFS.writeFile(path, JSON.stringify(getData()), 'utf8')
.then((success) => {
console.log('Success');
})
.catch((err) => {
console.log(err.message);
});
}
它运行良好,但在Android设备上却失败了。此错误显示:
Error: Directory could not be created
如果我试图在不创建目录的情况下编写一个普通文件,它将引发以下错误:
ENOENT: open failed: ENOENT (No such file or directory), open '/storage/emulated/0/data.json'
但是,我已经将这个权限添加到了我的AndroidManifest.xml
中
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
并在设置中授予外部存储权限。但是,如果我将RNFS.ExternalStorageDirectoryPath
更改为RNFS.DocumentDirectoryPath
,它可以正常工作,不会出现任何错误。但我需要进入外部存储器。有什么办法吗?
发布于 2020-08-13 11:48:17
我发现从Android 29+中需要对遗留的外部存储进行访问。因此,我的AndroidManifest.xml
(位于android/app/src/main/
中)就是这样的:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.appName">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<application
...
android:requestLegacyExternalStorage="true"
...
>
</application>
</manifest>
一切都开始运作了。此外,我还添加了向saveData
函数授予权限的请求:
const saveData = async () => {
try {
const granted = await PermissionsAndroid.requestMultiple([
PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE,
PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE,
]);
} catch (err) {
console.warn(err);
}
const readGranted = await PermissionsAndroid.check(PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE);
const writeGranted = await PermissionsAndroid.check(PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE);
if(!readGranted || !writeGranted) {
console.log('Read and write permissions have not been granted');
return;
}
var path = `${RNFS.ExternalStorageDirectoryPath}/MyApp`;
RNFS.mkdir(path);
path += '/data.json';
RNFS.writeFile(path, JSON.stringify(getData()), 'utf8')
.then((success) => {
console.log('Success');
})
.catch((err) => {
console.log(err.message);
});
}
发布于 2021-11-03 05:44:15
更新:使用作用域存储
或者,我找到了这个库响应-本机-文件访问,它使用的是作用域存储。在我的例子中,我将文件存储在下载目录中。
首先,使用RNSF,我们需要下载文件并在RNFS.TemporaryDirectoryPath
设置目标,然后将响应-本机-文件访问下载的文件复制到特定的文件夹(在我的例子中是下载)。
FileSystem.cpExternal(localFileUrl, `${filenameFormatted}`,'downloads')
也许您可以使用这个库将文件存储在特定的目录中,并进行一些调整。
使用权限管理外部存储(不推荐) :
在Android 11和更高版本中,访问存储的权限发生了变化。您必须添加名为MANAGE_EXTERNAL_STORAGE,的附加权限,请参阅以下文章:
https://developer.android.com/about/versions/11/privacy/storage#directory-access https://developer.android.com/training/data-storage/manage-all-files
将此添加到您的AndroidManifest.xml中
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
我创建了基于这个职位的简单允许请求,从function本机调用这个本机函数。
在PermissionFileModule.java中创建app/src/main/<your_package>
public class PermissionFileModule extends ReactContextBaseJavaModule implements ActivityEventListener {
public PermissionFileModule(@Nullable ReactApplicationContext reactContext) {
super(reactContext);
reactContext.addActivityEventListener(this);
}
@NonNull
@Override
public String getName() {
return "PermissionFile";
}
@ReactMethod
public void checkAndGrantPermission(Callback errorCallback, Callback successCallback) {
try {
if (!checkPermission()) {
requestPermission();
successCallback.invoke(false);
} else {
successCallback.invoke(true);
}
} catch (IllegalViewOperationException e) {
errorCallback.invoke(e.getMessage());
}
}
private boolean checkPermission() {
if (SDK_INT >= Build.VERSION_CODES.R) {
return Environment.isExternalStorageManager();
} else {
int result = ContextCompat.checkSelfPermission(getReactApplicationContext(), READ_EXTERNAL_STORAGE);
int result1 = ContextCompat.checkSelfPermission(getReactApplicationContext(), WRITE_EXTERNAL_STORAGE);
return result == PackageManager.PERMISSION_GRANTED && result1 == PackageManager.PERMISSION_GRANTED;
}
}
private void requestPermission() {
if (SDK_INT >= Build.VERSION_CODES.R) {
try {
Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
intent.addCategory("android.intent.category.DEFAULT");
intent.setData(Uri.parse(String.format("package:%s",getReactApplicationContext().getPackageName())));
getCurrentActivity().startActivityForResult(intent, 2296);
} catch (Exception e) {
Intent intent = new Intent();
intent.setAction(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);
getCurrentActivity().startActivityForResult(intent, 2296);
}
} else {
//below android 11
ActivityCompat.requestPermissions(getCurrentActivity(), new String[]{WRITE_EXTERNAL_STORAGE}, 100);
}
}
@Override
public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
if (requestCode == 2296) {
if (SDK_INT >= Build.VERSION_CODES.R) {
if (Environment.isExternalStorageManager()) {
Toast.makeText(getReactApplicationContext(), "Access granted", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(getReactApplicationContext(), "Access not granted", Toast.LENGTH_SHORT).show();
}
}
}
}
@Override
public void onNewIntent(Intent intent) {
// do nothing
}
}
在PermissionFilePackage.java中创建app/src/main/<your_package>
public class PermissionFilePackage implements ReactPackage {
@NonNull
@Override
public List<NativeModule> createNativeModules(@NonNull ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
modules.add(new PermissionFileModule(reactContext));
return modules;
}
@NonNull
@Override
public List<ViewManager> createViewManagers(@NonNull ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}
在MainApplication.java,中添加PermissionFilePackage.java作为附加包
...
@Override
protected List<ReactPackage> getPackages() {
@SuppressWarnings("UnnecessaryLocalVariable")
List<ReactPackage> packages = new PackageList(this).getPackages();
// Packages that cannot be autolinked yet can be added manually here, for example:
// packages.add(new MyReactNativePackage());
packages.add(new PermissionFilePackage());
return packages;
}
...
在RN组件中,像下面这样调用权限文件
...
import {NativeModules} from 'react-native';
var PermissionFile = NativeModules.PermissionFile;
...
if (Platform.Version >= 30) {
PermissionFile.checkAndGrantPermission(
(err) => {
DeviceUtils.showAlert(
'Sorry',
'Access not granted',
);
},
(res) => {
if (res) {
checkDirectoryAndDownload(url, name, ext);
}
},
);
} else {
DeviceUtils.grantPermissionSingleAndroid(
PERMISSIONS.ANDROID.WRITE_EXTERNAL_STORAGE,
(isAllow) => {
if (isAllow) {
checkDirectoryAndDownload(url, name, ext);
} else {
DeviceUtils.showAlert(
'Sorry',
'Access not granted',
);
}
},
);
}
发布于 2020-09-29 19:32:52
在Android 10 (API级别29)中,引入了范围存储的概念。据医生说-
“为了让用户更好地控制自己的文件,并限制文件杂乱无章,针对Android 10 (API级别29)及更高级别的应用程序默认被赋予作用域访问外部存储空间,或者作用域存储空间。这些应用程序只能访问外部存储中特定应用程序的目录,以及应用程序创建的特定媒体类型。”
对于Android 10,您可以选择退出作用域存储,方法是在android:requestLegacyExternalStorage="true"
中添加如下->
<application ...
android:requestLegacyExternalStorage="true"
...>
....
</application>
这是一个临时的解决方案,只适用于android版本10,而不是高于它。此外,请确保请求运行时权限读取/写入外部存储。
https://stackoverflow.com/questions/63392170
复制相似问题