anD*_*Dev 5 android react-native react-native-fs android-10.0
我的 React Native Android 应用程序需要将 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);
});
}
Run Code Online (Sandbox Code Playgroud)
它运行良好,但在 Android Q 设备上失败。显示此错误:
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"/>
Run Code Online (Sandbox Code Playgroud)
并在设置中授予外部存储权限。但如果我将其更改RNFS.ExternalStorageDirectoryPath为RNFS.DocumentDirectoryPath它,它就不会出现任何错误。但我需要访问外部存储。有什么办法可以做到吗?
我发现 Android API 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>
Run Code Online (Sandbox Code Playgroud)
一切都开始运转了。另外,我还添加了授予该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);
});
}
Run Code Online (Sandbox Code Playgroud)
更新:使用范围存储
或者,我找到了这个库react-native-file-access,它使用Scoped Storage。就我而言,我将文件存储在下载目录中。
首先,使用 RNSF,我们需要下载文件并将目的地设置为,然后使用react-native-file-accessRNFS.TemporaryDirectoryPath将下载的文件复制到特定文件夹(在我的例子中为下载)。
FileSystem.cpExternal(localFileUrl, `${filenameFormatted}`,'downloads')
Run Code Online (Sandbox Code Playgroud)
也许您可以使用这个库将文件存储在您的特定目录中,并进行一些调整
使用权限管理外部存储(不推荐):
在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" />
Run Code Online (Sandbox Code Playgroud)
我根据这篇文章创建简单的权限请求,从 React Native 调用这个本机函数。
创建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
}
}
Run Code Online (Sandbox Code Playgroud)
创建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();
}
}
Run Code Online (Sandbox Code Playgroud)
在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;
}
...
Run Code Online (Sandbox Code Playgroud)
在你的 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',
);
}
},
);
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
24993 次 |
| 最近记录: |