Expo FileSystem 现代文件系统 API 演进全解析:File/Directory 类式接口、FileHandle 与网络任务实战

发布时间:2026/9/10 2:25:47
Expo FileSystem 现代文件系统 API 演进全解析:File/Directory 类式接口、FileHandle 与网络任务实战 Expo FileSystem 现代文件系统 API 演进全解析File/Directory 类式接口、FileHandle 与网络任务实战【免费下载链接】expoAn open-source framework for making universal native apps with React. Expo runs on Android, iOS, and the web.项目地址: https://gitcode.com/GitHub_Trending/ex/expo导读expo-file-system是 Expo 生态中面向 Android、iOS 与 Web 的本地文件系统模块负责文件的读写、复制移动、下载上传、目录遍历与系统文件选择器集成。本文以 packages/expo-file-system/CHANGELOG.md 为骨架结合 src/File.ts、src/NetworkTasks.ts 等源码系统梳理 19.0.0 之后现代 API 成为默认这一架构分水岭完整覆盖File/Directory/Paths类式接口、FileHandle低级随机访问、异步化迁移、下载上传任务、文件选择与预览、Blob/流式接口、目录监视、SAF 与安全修复。读完本文你将掌握新版 expo-file-system 的完整 API 面、每个关键参数的取值与默认值以及从旧 API 平滑迁移的完整清单。一、架构分水岭现代 API 成为默认19.0.0CHANGELOG 中最具决定意义的一条出现在19.0.02025-08-13Make the modern filesystem API the default, move previous one toexpo-file-system/legacy.这意味着自 SDK 19 起包默认导出的是基于SharedObject的类式 APIFile、Directory、Paths而 2024 年之前以FileSystem.xxxAsync函数形式存在的旧 API 全部下沉到expo-file-system/legacy子路径。这一点在 package.json 的exports字段中有清晰的落地exports: { .: { types: { expo-source: ./src/index.ts, default: ./build/index.d.ts }, expo-source: ./src/index.ts, default: ./build/index.js }, ./legacy: { ... }, ./next: { ... } }现代 API 并非一蹴而就而是在 18.0.02024-10-22起陆续孵化当时仍叫file-system/nextCHANGELOG 记录了它的成长轨迹18.0.0加入路径工具与parentDirectory、extension字段exists()函数改为exists属性新增name属性、base64()、size/md5属性、目录内容列举、createFile/createDirectory、文件下载、复制与移动、文件句柄、.bytes()与写入Uint8Array、Blob 支持与.blob()函数。19.0.0加入文件/目录选择器、asset URI 支持、File直接实现 Blob 接口、目录信息函数、totalDiskSpace/availableDiskSpace/目录尺寸、info()方法、modificationTime/creationTime属性、downloadFileAsync自定义请求头、SAF URI 完整支持。在源码层面包根入口 src/index.ts 导出的正是这套现代 APIPaths、File、Directory、UploadTask/DownloadTask以及FileMode、EncodingType、UploadType等枚举与全部类型。旧代码如需继续使用函数式 API改为import * as FileSystem from expo-file-system/legacy;二、类式 API 核心File / Directory / Paths2.1 File一个不必须存在的文件对象File实例可以为任意路径创建创建时文件并不需要真实存在见 src/File.ts。构造函数接受字符串 URI、File实例与Directory实例的任意组合底层通过Paths.join(...)完成路径连接import { File, Directory, Paths } from expo-file-system; const file new File(Paths.cache, subdirName, file.txt); const dir new Directory(Paths.document); const file2 new File(dir, notes, hello.md);常用的属性与同步方法由 src/File.ts 与原生实现 FileSystemFile.kt、FileSystemFile.swift 提供parentDirectory所在目录的Directory实例extension含点如.pngname含扩展名的文件名。size文件字节数null表示文件缺失或不可读CHANGELOG 明确修复了该属性对缺失/不可读文件错误返回的问题。exists、typeMIME 类型、lastModified替代旧modificationTime对齐 WebFile接口后者已标记废弃。text()/textSync()、bytes()/bytesSync()、base64()/base64Sync()三种读取形态并支持EncodingType.UTF8与EncodingType.Base64两种编码。write(contents, options)/writeSync()异步/同步写入FileWriteOptions支持encoding默认 UTF8与append默认falseappend: true追加到文件末尾55.0.5 新增。copy(destination, options)/copySync()、move(destination, options)/moveSync()异步/同步复制与移动RelocationOptions提供overwrite默认false。create(options)/delete()创建与删除FileCreateOptions提供intermediates是否创建缺失的中间目录默认false与overwrite默认false。info(options)返回FileInfo包含exists、uri、size、modificationTime、creationTimeAndroid API 26 之前为null与可选的md5。rename(name)重命名19.0.7 新增。2.2 Directory目录即对象Directory同样支持任意路径构造与多段路径连接src/Directory.tsconst dir new Directory(Paths.cache, subdirName);关键能力list()返回(Directory | File)[]父目录不存在时抛错info()/listAsRecords()返回底层记录。createFile(name, mimeType)、createDirectory(name)在目录内创建文件与子目录创建目录支持idempotent选项19.0.7 新增重复创建不再报错。create(options)同样支持intermediates/overwritedelete()支持递归删除19.0.17 修复 Android 递归删除。pickDirectoryAsync(initialUri?)打开系统目录选择器19.0.11 起 iOS 支持。name、parentDirectory、exists、size等属性。2.3 Paths内置目录与磁盘信息src/Paths.ts 提供一组静态入口成员说明Paths.cache缓存目录系统低存储时可能被清理Paths.document文档目录系统不会随意删除Paths.bundle应用捆绑资源目录Paths.appleSharedContainersiOS 上 Apple App Groups 共享容器路径按 groupId 返回Directory映射Paths.totalDiskSpace内部存储总空间字节Paths.availableDiskSpace内部存储可用空间字节Paths.info(...uris)判断路径是否为目录注意56.0.7在 Expo Go 中Paths.cache与Paths.document已改为指向 experience 隔离目录即每个项目独享的沙箱目录并修复了此前 Expo Go 中这两个路径指向错误目录的问题。独立构建dev client / release build不受影响。Paths.join、Paths.dirname、Paths.basename、Paths.extname等路径工具由PathUtilities提供src/pathUtilities/index.ts。三、异步化迁移write、copy、move 与 FileHandle 的同步/异步双轨现代 API 刻意区分异步不阻塞 JS 线程与同步阻塞 JS 线程两类方法且命名规范为异步方法不带后缀、同步方法带Sync后缀。CHANGELOG 记录了两波关键迁移56.0.02026-05-05copy()andmove()methods ofFileandDirectoryare now asynchronous and return a Promise. UsecopySync()andmoveSync()for synchronous behavior.Unpublished当前主分支对应 SDK 57File.write()is now asynchronous and returns a Promise. UseFile.writeSync()for synchronous behavior.FileHandle.readBytes()andFileHandle.writeBytes()are now asynchronous and return a Promise. UseFileHandle.readBytesSync()andFileHandle.writeBytesSync()for synchronous behavior.迁移对照表旧写法新异步写法新同步写法file.write(data)返回 voidawait file.write(data)file.writeSync(data)file.copy(dest)await file.copy(dest)file.copySync(dest)file.move(dest)await file.move(dest)file.moveSync(dest)handle.readBytes(n)await handle.readBytes(n)handle.readBytesSync(n)handle.writeBytes(b)await handle.writeBytes(b)handle.writeBytesSync(b)原生侧为此做了配套优化Android 上的读写尽可能切换至Dispatchers.IO执行CHANGELOG #46376、#47945避免阻塞主线程同时修复了异步与同步FileHandle操作在 Android/iOS 上重叠执行时的文件偏移竞争问题#47945。同一句柄上多个异步操作不保证按调用顺序完成如需顺序性务必逐个await。四、FileHandle 与 FileMode低级随机访问FileHandle提供基于字节偏移的随机读写能力通过file.open(mode)获取类型定义见 src/File.types.ts原生实现见 FileSystemFileHandle.kt 与 FileSystemFileHandle.swift。4.1 FileMode 五种模式56.0.0 起双端一致枚举值字符串说明FileMode.ReadWriterw读写游标在文件开头不能用于 SAFcontent://URIFileMode.ReadOnlyr只读游标在文件开头FileMode.WriteOnlyw只写游标在文件开头FileMode.Appendwa只写游标在文件末尾对 SAF 文件是严格追加模式seek()无效FileMode.Truncatewt只写并截断为 0 字节清空内容mode选项在 56.0.0 中分别由 Android#42983与 iOS#44559原生侧补齐支持。4.2 FileHandle 核心 APIimport { File, Paths, FileMode } from expo-file-system; const file new File(Paths.cache, data.bin); const handle file.open(FileMode.ReadOnly); const header handle.readBytesSync(4); // 同步读 4 字节 handle.offset 100; // 任意 seek const chunk await handle.readBytes(50); // 异步读 50 字节 handle.close(); // 必须关闭readBytes(length)/readBytesSync(length)从当前偏移读取返回值可能少于length读到文件末尾偏移已过末尾时返回空Uint8Array单次读取上限取决于平台ArrayBuffer大小Android 约 2 GBiOS 为 64 位上限超大文件需循环读取。writeBytes(bytes)/writeBytesSync(bytes)在当前偏移写入并推进偏移。offset当前字节偏移可写seek大于文件尺寸时下次写入追加到末尾。关闭后为null。size文件总字节数关闭后为null。close()释放底层文件描述符。忘记关闭可能阻止文件被删除、移动或被其他进程打开关闭后再调用读写方法会抛错。CHANGELOG 还记录了FileHandle的细节打磨修复 security-scoped 访问与非 SAFcontent://URI 支持#47176、改进文档#46849以及 Jest mock 对FileMode字符串值的严格校验56.0.6。五、文件摘要digest()全面取代md5Unpublished 版本新增AddedFile.digest()for asynchronously calculating MD5, SHA-1, SHA-256, SHA-384 and SHA-512 file digests.const md5 await file.digest(MD5); const sha256 await file.digest(SHA-256);FileDigestAlgorithm MD5 | SHA-1 | SHA-256 | SHA-384 | SHA-512src/File.types.ts。与此同时File.md5属性以及info()相关的 MD5 类型InfoOptions、InfoOptions.md5、FileInfo.md5全部标记为deprecated统一引导到await file.digest(MD5)。历史版本还修复过md5计算在大文件下的内存溢出问题#44064新版digest()采用流式计算天然规避该问题。六、网络任务下载、上传与断点续传6.1 File.downloadFileAsync()静态方法直接下载文件src/File.tsconst file await File.downloadFileAsync( https://example.com/image.png, new Directory(Paths.document), // 目标目录或 File { headers: { Authorization: Bearer xxx }, idempotent: true, // 覆盖已存在文件false 则抛 DestinationAlreadyExists onProgress: ({ bytesWritten, totalBytes }) console.log(bytesWritten, totalBytes), signal: abortController.signal, // 取消下载 } );DownloadOptionssrc/NetworkTasks.types.ts要点headers自定义请求头19.0.0 起支持。idempotent19.0.15 新增true覆盖已存在文件false目标已存在时以DestinationAlreadyExists拒绝。onProgress进度回调totalBytes在服务端未返回Content-Length时为-1完成时会补发一次 100% 的合成进度事件源码中显式处理了小文件不触发原生进度事件、以及事件与 Promise 竞态的问题。signal中止信号中止后 Promise 以AbortError拒绝并调用原生cancelDownloadAsync取消传输。非 2xx 响应会以包含状态码的UnableToDownload错误拒绝且不会创建文件。平台行为差异源码 JSDoc 明确记录Android 将响应体直接流式写入目标文件下载失败后可能残留半成品文件iOS 先在临时位置完成下载、成功后才移动到目标位置失败时不留文件。6.2 UploadTask 与 File.upload()56.0.0 起引入任务式上传#44055、#45033const task file.createUploadTask(https://example.com/upload, { httpMethod: POST, // POST | PUT | PATCH默认 POST uploadType: UploadType.MULTIPART, // BINARY_CONTENT0 | MULTIPART1 fieldName: file, // multipart 字段名默认 file mimeType: image/jpeg, parameters: { note: from-expo }, // 附加表单参数 sessionType: background, // iOS 专用默认 background onProgress: ({ bytesSent, totalBytes }) console.log(bytesSent, totalBytes), signal, }); const result await task.uploadAsync(); // { body, status, headers }也可以直接await file.upload(url, options)等价于创建任务并立即uploadAsync()。UploadResult包含body响应体字符串、statusHTTP 状态码与headers对任何已完成的 HTTP 响应含非 2xx都会 resolve仅在文件不可读、请求失败或取消时 reject。multipart 上传的进度字节可能包含边界字符串、请求头与表单参数等 framing 开销。6.3 DownloadTask可暂停/恢复的下载const task File.createDownloadTask(url, new File(Paths.document, video.mp4), { onProgress: ({ bytesWritten, totalBytes }) console.log(bytesWritten, totalBytes), sessionType: background, }); const file await task.downloadAsync(); await task.pauseAsync(); // 进入 paused得到 resumeData const state task.savable(); // 可持久化的暂停状态 // 之后可恢复 const restored DownloadTask.fromSavable(savedState, { onProgress }); const file2 await restored.resumeAsync();任务状态机idle → active → (paused | completed | cancelled | error)src/NetworkTasks.ts 中的UploadTaskState/DownloadTaskState。savable()返回的DownloadPauseState包含url、fileUri、isDirectory、headers与平台相关的resumeData不包含回调与信号可安全持久化fromSavable()重建任务并允许附加新的进度回调与中止信号。6.4 网络层历史修复CHANGELOG 记录了网络层多项关键修复downloadAsync修复本地文件下载iOS、上传请求httpMethod缺失、uploadAsync在BINARY_CONTENT下无法 resolve、totalByteSent字段重命名与错误发送bytesSent的问题、Android 上Double cannot be cast to Integer崩溃、downloadResumableStartAsync改用addInterceptor等。这些修复共同保证了上传下载链路的健壮性。七、文件选择与预览7.1 File.pickFileAsync()56.0.0 起与expo-document-picker功能对齐支持多选与多 MIME 类型const result await File.pickFileAsync({ initialUri: new Directory(Paths.document).uri, // 初始目录 mimeTypes: [image/*, application/pdf], // 支持通配符默认 */* multipleFiles: true, // 是否多选默认 false }); if (!result.canceled) { for (const file of result.result) console.log(file.uri); }单文件结果{ result: File, canceled: false }多文件结果{ result: File[], canceled: false }取消返回{ result: null, canceled: true }见 src/File.types.ts 中PickSingleFileResult/PickMultipleFilesResult。旧签名pickFileAsync(initialUri, mimeType)已废弃改为统一的 options 对象形式CHANGELOG #43411。iOS 上选择返回原文件的临时副本原始文件保持不变。相关修复iOS 通配符 MIME*/*、image/*导致文件全部灰显的问题#45245。7.2 preview() 与 canPreview()Unpublished 版本新增文件预览能力原生实现见 FileSystemPreview.swiftfile.canPreview(options?)判断平台能否预览——iOS 检查 Quick Look 是否能处理Android 检查是否有应用能处理该 MIME 类型的ACTION_VIEWintent文件不存在时 resolvefalse无效或不可读文件会 reject。file.preview(options?)打开平台原生预览——iOS 呈现 Quick LookAndroid 启动ACTION_VIEWPromise 在预览已呈现或移交给其他应用时 resolve。选项FilePreviewOptions支持title展示标题与mimeTypeAndroid 用于匹配应用缺省用文件type。相关修复iOS 上上一次 Quick Look 关闭动画未结束时新预览被拒绝的问题#47947。八、Blob 接口与流式读写56.0.6 起File完整实现 WebBlob接口CHANGELOG #38160 起逐步推进18.0.6 已加入.blob()与 Blob 支持// File 本身就是一个 Blob可直接用于网络请求 const form new FormData(); form.append(file, new File(Paths.cache, photo.jpg), photo.jpg); await fetch(url, { method: POST, body: form }); const buf await file.arrayBuffer(); // ArrayBuffer const obj await file.json(); // JSON.parse(file.text()) const fd await file.formData(); // 通过 Response 解析 const stream file.stream(); // 别名 readableStream() const slice file.slice(0, 100, image/png); // 分片为新的 Blob流式接口src/internal/streams.tsfile.readableStream()基于FileHandle的字节流默认按 1024 字节分块读取流被消费完或取消时自动关闭句柄。file.writableStream()接受Uint8Array分块的写流关闭或中止时自动关闭句柄。Unpublished 修复了流的一个隐蔽 bug当 BYOB 读取目标视图起点为非零偏移时readableStream()会返回清零字节并越界写入请求区域#49234。九、目录监视watch()56.0.0 起支持监听文件/目录变化#44986const sub file.watch((event) console.log(event.type, event.target.uri)); const dirSub new Directory(Paths.cache).watch((event) { console.log(${event.type}: ${event.target.uri}); }); // 停止监听 sub.remove();WatchEvent包含事件类型如created、modified、deleted、renamed与目标对象WatchOptions支持防抖DEFAULT_DEBOUNCE_MS见 src/FileSystem.types.ts与事件过滤。文件或目录被删除/重命名时 watcher 自动停止。iOS 限制源码 JSDoc 明确子项变化以目录上的粗粒度modified事件呈现按子级created/deleted/renamed过滤不可靠。测试覆盖见 src/tests/FileSystemWatcher-test.ts 与原生 FileSystemWatcher.kt。十、Android 平台细节SAF、权限与安全修复10.1 SAF 与 content:// URI18.0.0 起逐步加入对 SAFStorage Access FrameworkURI 的有限支持到完整支持#3807556.0.0 修复 copy/move 对 SAF 与 content provider URI 的支持#42887。File/Directory暴露contentUri属性19.0.19 Android 新增FileSystemFileProviderFileSystemFileProvider.kt配合file_system_provider_paths.xml生成content://分享 URI。Unpublished 修复了 SAF 上传性能问题请求体内容长度只解析一次而不是每写入 8 KiB 就通过ContentResolver查询一次#49206。注意FileMode.ReadWriterw不适用于 SAFcontent://URISAF 下Append为严格追加seek()无效。10.2 权限与安全修复56.0.6 集中落地为delete()与openHandle()补充缺失的权限检查#45967。修复createFile、createDirectory、rename的路径穿越漏洞防止通过构造路径逃逸父目录#45967。修复rename()保存未编码 URI 的问题文件名含空格时读取.uri会抛错#48510。55.0.0 为READ_EXTERNAL_STORAGE/WRITE_EXTERNAL_STORAGE添加android:maxSdkVersion注解并移除对旧版原生模块 API 的引用。55.0.6 修复 bundle 目录列举返回错误的文件名、URI 与尾斜杠的问题。10.3 配置插件56.0.0 起暴露带类型的 config plugin 函数#44098用于在预构建阶段注入文件共享等配置iOS 文件共享配置见 19.0.16#39286实现位于 plugin/src/withFileSystem.ts。十一、Jest Mock内存文件系统的可测试性56.0.0 起为类式File/Directory/PathsAPI 提供行为级 Jest mock#45027基于内存文件系统实现测试可以直接完成 create/write/read/move/copy/delete 往返无需手写jest.mockmock 实现位于 mocks/FileSystem.ts。56.0.6 强化了FileSystemFileHandlemock严格按FileMode字符串值r、w、wa、wt、rw而非废弃的名称表分发暴露句柄与文件元数据并与文档化的content://URI 默认模式保持一致分发对FileMode穷举未来新增枚举值在 mock 中编译失败从类型层面防止遗漏。十二、平台版本要求与历史基线现代 API 面向 Android、iOS、macOS 与 Web19.0.11 起加入 minimal web stub 修复 Web 导入。CHANGELOG 明确的最低版本基线56.0.0最低 iOS/tvOS 16.4、macOS 13.4#43296。历史18.0.0 提升 iOS/tvOS 到 15.115.9.0 提升 iOS 到 13.4、AndroidcompileSdkVersion/targetSdkVersion到 3415.8.0 放弃 Android SDK 21/2215.7.0 支持 Apple tvOS16.0.3 支持 macOS18.1.0 最低 macOS 11.0。包导出新增./next子路径以消除 Metro 打包器警告Unpublished#44793。结语升级与迁移清单基于 CHANGELOG 与源码从旧 API 迁移到现代 API 时请依次核对导入路径expo-file-system新与expo-file-system/legacy旧二选一避免混用。函数式 → 类式FileSystem.readAsStringAsync(uri)→new File(uri).text()FileSystem.makeDirectoryAsync→new Directory(uri).create()。异步化write/copy/move/readBytes/writeBytes均改为返回 Promise同步场景改用带Sync后缀的方法。字段更名modificationTime→lastModifiedmd5→await file.digest(MD5)FileSystem.getInfoAsync→file.info()/Paths.info()。文件选择器pickFileAsync(uri, mimeType)旧签名 →pickFileAsync({ initialUri, mimeTypes, multipleFiles })。资源释放使用FileHandle与网络任务后及时close()/release()。现代 API 的所有行为细节都能在 src/File.ts、src/Directory.ts、src/NetworkTasks.ts 的类型与 JSDoc 中找到精确描述配合 CHANGELOG.md 可以完整还原每一处行为变更的来龙去脉。【免费下载链接】expoAn open-source framework for making universal native apps with React. Expo runs on Android, iOS, and the web.项目地址: https://gitcode.com/GitHub_Trending/ex/expo创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询