Web 流式写入文件
背景
由于吾辈之前使用的一个域名即将到期,需要将 IndexedDB 数据迁移到新的域名,因此这两天创建了一个新的浏览器扩展 IDBPort,用于迁移 IndexedDB 数据到其他域名。而在迁移数据时,需要将数据导出为并下载到本地,然后导入到新的域名。由于数据量较多,同时包含一些图像之类的二进制数据,所以需要使用流式写入的方式来避免内存占用过高。
首先,Web 中有什么 Target 可以流式写入数据吗?
实际上,是有的,例如 Blob+Response,或者 OPFS 私有文件系统,它们都支持流式写入数据到磁盘,考虑到 OPFS 仍然相对较新,所以下面使用 Blob+Response 来实现。
流式写入
如果不考虑流式写入,可以将数据全部都放在内存中的话,那么直接使用一个 string[] 来存储数组,然后一次性创建一个 Blob 对象,也是一种选择。但如果数据有数百 M(包含图像或视频)甚至上 G,那么内存就会爆掉,需要使用流式写入保持内存不会线形增长。在之前 在 Web 中解压大型 ZIP 并保持目录结构 中有提到过,但由于当时使用 zip.js,而它们直接提供了 BlobWriter/BlobReader 来使用,所以并未深入研究实现,也没有尝试手动实现。这里涉及到几个关键 API
- Blob: 二进制数据存储接口,它会在数据过多时透明的从内存转移到磁盘 [1],这保证了内存占用不会太大
- Response: Response 允许接收一个 ReadableStream 并创建一个 Blob 对象
- TransformStream:提供一个通道,提供一个 ReadableStream 和 WritableStream,让流式写入变的简单
- TextEncoderStream: 将一个文本流转换为 Uint8Array 流,这是 Response ReadableStream 所需要的数据格式
基本流程
- 创建 TransformStream
- 使用 ReadableStream 结合 TextEncoderStream 创建 Response
- 立刻获取 blob,触发 ReadableStream 的拉取
- 使用 WritableStream 开始写入
- 关闭 TransformStream
- await promise blob 来获取写入完成的 blob
10 行代码即可验证
1 | |
流式读取
相比之下,流式读取使用的 API 要更少,只需要使用 blob.stream() 即可流式读取一个 Blob(或者一个一个 File)。几个关键的 API
- TextDecoderStream: 将一个 Uint8Array 字节流转换为文本流
由于 blob.stream() 返回的 chunk 可能存在截断或不完整,例如假使预期的 chunk 是按照换行分割点文本 line1\nline2\n,blob.stream() 可能会返回 line1 甚至截断的...
剩余内容已隐藏