React Native选择外部目录并读取内容报错问题求助
解决RNFS读取DocumentPicker返回目录URI时的「Folder does not exist」报错
问题根源
react-native-document-picker返回的content://格式URI是Android**存储访问框架(SAF)**提供的内容URI,并非传统的文件系统路径。而react-native-fs(RNFS)的readDir方法仅支持解析真实的文件系统路径(如/storage/emulated/0/Download/IRCTC ERS),直接传入content URI自然会提示目录不存在。
解决方案
根据Android版本和需求,可选择以下方案:
方案1:将Content URI转换为真实文件路径(Android 10及以下优先)
使用第三方库将content URI转换为文件系统路径,推荐react-native-get-real-path:
- 安装依赖
npm install react-native-get-real-path --save # 或 yarn add react-native-get-real-path
- 修改代码实现转换
import PermissionsAndroid from 'react-native/Libraries/PermissionsAndroid'; import DocumentPicker from 'react-native-document-picker'; import RNFS from 'react-native-fs'; import RealPath from 'react-native-get-real-path'; const readSelectedDirectory = async () => { // 请求存储权限 const per = await PermissionsAndroid.request( PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE ); if (per !== PermissionsAndroid.RESULTS.GRANTED) return; // 选择目录 const res = await DocumentPicker.pickDirectory(); // 转换content URI为真实路径 const realFilePath = await RealPath.getRealPathFromURI(res.uri); // 读取目录 const files = await RNFS.readDir(realFilePath); console.log('目录文件列表:', files); };
注意:Android 11及以上因Scoped Storage限制,部分目录无法获取真实路径,此方案可能失效。
方案2:通过Android原生API直接遍历Content URI对应的目录(全版本兼容)
由于RNFS不支持SAF的content URI,可编写原生模块调用Android的DocumentFile API遍历目录:
- 创建Android原生模块(示例核心代码)
import android.content.Context; import android.net.Uri; import androidx.documentfile.provider.DocumentFile; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactContextBaseJavaModule; import com.facebook.react.bridge.ReactMethod; import com.facebook.react.bridge.Promise; import org.json.JSONArray; import org.json.JSONObject; public class SAFDirectoryReaderModule extends ReactContextBaseJavaModule { private final ReactApplicationContext reactContext; public SAFDirectoryReaderModule(ReactApplicationContext reactContext) { super(reactContext); this.reactContext = reactContext; } @Override public String getName() { return "SAFDirectoryReader"; } @ReactMethod public void listFiles(String treeUri, Promise promise) { try { Uri uri = Uri.parse(treeUri); DocumentFile directory = DocumentFile.fromTreeUri(reactContext, uri); JSONArray fileArray = new JSONArray(); if (directory != null && directory.isDirectory()) { for (DocumentFile file : directory.listFiles()) { JSONObject fileObj = new JSONObject(); fileObj.put("name", file.getName()); fileObj.put("uri", file.getUri().toString()); fileObj.put("isDirectory", file.isDirectory()); fileArray.put(fileObj); } } promise.resolve(fileArray.toString()); } catch (Exception e) { promise.reject("SAF_ERROR", e.getMessage()); } } }
- 在RN端调用原生模块
import { NativeModules } from 'react-native'; const { SAFDirectoryReader } = NativeModules; // 选择目录后调用 const res = await DocumentPicker.pickDirectory(); const fileListStr = await SAFDirectoryReader.listFiles(res.uri); const fileList = JSON.parse(fileListStr); console.log('目录文件列表:', fileList);
方案3:改用支持SAF的文件操作库
替换RNFS为支持content URI的库,例如react-native-document-file-picker,该库可直接处理SAF的目录URI,无需额外转换。
额外注意事项
- Android 11+:若需访问所有外部存储,需申请
MANAGE_EXTERNAL_STORAGE权限,但该权限需用户手动在系统设置中开启,且Google Play对其使用有严格限制,仅特定场景允许。 - 权限配置:确保
AndroidManifest.xml中已添加android:requestLegacyExternalStorage="true"(仅Android 10有效),并正确声明存储权限。
内容的提问来源于stack exchange,提问作者Ravi
相关产品推荐
相关产品推荐

