首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >使用将文件写入Android外部存储器

使用将文件写入Android外部存储器
EN

Stack Overflow用户
提问于 2020-08-13 09:31:40
回答 3查看 16.8K关注 0票数 5

我的应用程序需要在外部存储中的特殊文件夹中保存一个JSON文件。我试图以这种方式使用RNFS (https://github.com/itinance/react-native-fs)来实现它:

代码语言:javascript
运行
复制
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

代码语言:javascript
运行
复制
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

并在设置中授予外部存储权限。但是,如果我将RNFS.ExternalStorageDirectoryPath更改为RNFS.DocumentDirectoryPath,它可以正常工作,不会出现任何错误。但我需要进入外部存储器。有什么办法吗?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2020-08-13 11:48:17

我发现从Android 29+中需要对遗留的外部存储进行访问。因此,我的AndroidManifest.xml (位于android/app/src/main/中)就是这样的:

代码语言:javascript
运行
复制
<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函数授予权限的请求:

代码语言:javascript
运行
复制
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);
      });
  }
票数 5
EN

Stack Overflow用户

发布于 2021-11-03 05:44:15

更新:使用作用域存储

或者,我找到了这个库响应-本机-文件访问,它使用的是作用域存储。在我的例子中,我将文件存储在下载目录中。

首先,使用RNSF,我们需要下载文件并在RNFS.TemporaryDirectoryPath设置目标,然后将响应-本机-文件访问下载的文件复制到特定的文件夹(在我的例子中是下载)。

代码语言:javascript
运行
复制
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中

代码语言:javascript
运行
复制
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />

我创建了基于这个职位的简单允许请求,从function本机调用这个本机函数。

PermissionFileModule.java中创建app/src/main/<your_package>

代码语言:javascript
运行
复制
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>

代码语言:javascript
运行
复制
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作为附加包

代码语言:javascript
运行
复制
...
@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组件中,像下面这样调用权限文件

代码语言:javascript
运行
复制
...
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',
              );
            }
          },
        );
      }
票数 4
EN

Stack Overflow用户

发布于 2020-09-29 19:32:52

在Android 10 (API级别29)中,引入了范围存储的概念。据医生说-

“为了让用户更好地控制自己的文件,并限制文件杂乱无章,针对Android 10 (API级别29)及更高级别的应用程序默认被赋予作用域访问外部存储空间,或者作用域存储空间。这些应用程序只能访问外部存储中特定应用程序的目录,以及应用程序创建的特定媒体类型。”

对于Android 10,您可以选择退出作用域存储,方法是在android:requestLegacyExternalStorage="true"中添加如下->

代码语言:javascript
运行
复制
<application ...
 android:requestLegacyExternalStorage="true"
...>
....
</application>

这是一个临时的解决方案,只适用于android版本10,而不是高于它。此外,请确保请求运行时权限读取/写入外部存储。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/63392170

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档