React Native 文件选择实战:react-native-document-picker 从选文件到另存为的完整指南
【免费下载链接】react-native-document-pickerDocument Picker and Viewer for React Native项目地址: https://gitcode.com/gh_mirrors/re/react-native-document-picker
做个"导入 PDF 合同"功能时,90% 的坑不在 UI,而在拿到那个文件 URI 之后:有的 URI 是临时授权、App 一杀进程就读不到了;有的文件是 Google Docs 这种"虚拟文件",根本没有实体;还有个平台的文件管理器压根不遵守你的类型过滤。react-native-document-picker(包名@react-native-documents/picker)封装了 iOS 的 UIDocumentPicker 和 Android 的 Storage Access Framework,把文件选择、类型过滤、多选、另存为、长期访问权限这几件事统一成一套 JS API。
30 秒接入:装包 + 第一次 pick
yarn add @react-native-documents/pickeriOS 项目装完在ios/目录执行pod install,Android 无需额外配置。Expo 项目不能跑在 Expo Go 里(含自定义原生代码),需要走expo prebuild --clean打开发构建。
然后是最小可用的选择逻辑:
import DocumentPicker from '@react-native-documents/picker' const onPick = async () => { try { const res = await DocumentPicker.pick() // 不传 type = 全部文件 console.log(res[0].uri, res[0].name, res[0].size) } catch (err) { if (DocumentPicker.isCancel(err)) return // 用户点了取消,不算错误 console.error(err) } }注意两点:结果总是数组(哪怕只选了一个);用户取消会走 reject,所以必须 catch 并用isCancel区分"取消"和"真报错"。
场景一:只收 PDF,但别全信文件管理器
合同上传、简历投递这类场景要限定类型。库内置了常用 MIME/UTI 常量(Android 映射为 MIME,iOS 映射为 UTType,一套代码双端自动切换):
await DocumentPicker.pick({ type: [DocumentPicker.types.pdf], })全部预定义常量(pdf、images、video、docx、xlsx、allFiles…)在packages/document-picker/src/fileTypes.ts,也可以直接传任意字符串。
坑:Android 上部分第三方文档提供者会无视类型过滤,用户仍能挑一个 .docx 出来。响应里的hasRequestedType字段为false时说明用户挑的文件不在你要的类型里,这种情况自己做二次校验并提示用户,别默默收下。
场景二:批量导入素材,一次挑 N 个
图片编辑、素材库应用需要用户一次挑几十张图,加一个开关即可:
const res = await DocumentPicker.pick({ type: [DocumentPicker.types.images], allowMultiSelection: true, }) res.forEach(f => console.log(f.uri, f.size))每个元素都是完整的元数据对象:uri、name、type、size、nativeType,直接拿来建索引,不用再逐个读文件。
场景三:把生成的文件另存到用户目录
上传完文件,反过来还要"写出去"——比如把导出的报告存到用户的 Documents。saveDocuments会拉起系统的"另存为"对话框,让用户自己挑位置:
const res = await DocumentPicker.saveDocuments({ sourceUris: [reportUri], fileName: '2026-Q2-报告.pdf', // Android 端预填文件名 })平台差异记住一句话就够:Android 一次只能存 1 个文件,iOS 可以多存;fileName只在 Android 生效(iOS 的名字取自源文件,仅单文件时用户可改);mimeType也是 Android 专用,建议提供,省得系统去猜。
import 还是 open:文件生命周期怎么选
这是最容易踩错的地方,直接给结论:
| 模式 | 行为 | 适用场景 |
|---|---|---|
mode: 'import'(默认) | 文件复制到 App 沙盒,长期可读可上传 | 上传服务器、离线缓存 |
mode: 'open' | 临时授权,App 被杀后失效 | 只读预览一次 |
mode: 'open'+requestLongTermAccess: true | 返回bookmark,重启后凭它重新访问 | 需要跨启动访问同一文件 |
三个模式的行为对比见下图,限制为 PDF 的导入模式:
另外两个配套 API:
keepLocalCopy:把content://URI 或虚拟文件(Google Docs 之类)导出成沙盒里的实体文件,目标目录可选cachesDirectory/documentDirectory。上传场景其实用不到它——fetch直接支持content://URI,能省一步省一步。pickDirectory:选文件夹而不是文件,同样支持短期/长期授权。
双端配置清单:好消息是基本不用配
| 项目 | Android | iOS |
|---|---|---|
| 运行时存储权限 | 不需要(走 SAF,无需 READ/WRITE_EXTERNAL_STORAGE) | 不需要 |
| Info.plist 描述文案 | — | 不需要(系统选择器自带授权流) |
| 额外依赖 | 无 | pod install |
| 虚拟文件(云文档) | allowVirtualFiles: true开启,响应带isVirtual和convertibleToMimeTypes | 无此概念 |
很多旧教程让你加存储权限、改requestLegacyExternalStorage,那是 SAF 之前的做法,现在加只会惹审计麻烦。
真正的平台差异集中在响应字段:Android 的uri是content://,iOS 是file://;nativeType在 Android 是 MIME、在 iOS 是 UTI。跨端逻辑只认uri+type,别碰平台私有字段。
报错对照表:四种 code 各是什么情况
catch (err) { if (DocumentPicker.isErrorWithCode(err)) { switch (err.code) { case DocumentPicker.errorCodes.OPERATION_CANCELED: // 用户取消 case DocumentPicker.errorCodes.IN_PROGRESS: // 已有选择器在弹 case DocumentPicker.errorCodes.UNABLE_TO_OPEN_FILE_TYPE: // 类型无法解析 case DocumentPicker.errorCodes.NULL_PRESENTER: // 无可用 UI 上下文 } } }- 取消(
OPERATION_CANCELED):静默处理,不要弹错误提示。 IN_PROGRESS:按钮防抖,或选择器还没关就再次点击了。UNABLE_TO_OPEN_FILE_TYPE:检查传给type的字符串是不是合法的 MIME/UTI。
上手就干三件事
先跑通pick()+isCancel这条最小链路,再按你的业务挑一条线深入:要传服务器走 import 模式,要长期引用换 long-term access,要写出去用saveDocuments。完整参数表在 安装与配置文档,原生实现参考 Android 模块源码 和 iOS 模块源码。
【免费下载链接】react-native-document-pickerDocument Picker and Viewer for React Native项目地址: https://gitcode.com/gh_mirrors/re/react-native-document-picker
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考