ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

js-ipfs 0.48.0 API 迁移指南:从回调、Streams 与缓冲式 API 全面迁移到 Async/Await 与 Async Iterables

2026/9/28 7:23:27 拓冰建站 浏览量
js-ipfs 0.48.0 API 迁移指南:从回调、Streams 与缓冲式 API 全面迁移到 Async/Await 与 Async Iterables 存储网络通信【免费下载链接】js-ipfsIPFS implementation in JavaScript项目地址https://gitcode.com/gh_mirrors/js/js-ipfs点击查看免费下载本文基于 docs/MIGRATION-TO-ASYNC-AWAIT.md 编写面向正在使用 js-ipfs或 js-ipfs-http-client并准备升级到 0.48.0 新核心 API 的开发者。0.48.0 对 JS IPFS 核心 API 做了一次彻底的重设计回调callback不再被支持PeerId/PeerInfo实例不再直接返回Pull Streams、Node.js Streams 以及旧的缓冲式buffering方法*ReadableStream、*PullStream、addFromFs、addFromURL、addFromStream等被移除取而代之的是语言原生的Async Iterables异步可迭代对象。读完本文你将掌握如何用 async/await 或callbackify平滑替换回调代码、如何将 peer 相关数据结构在新旧形态间互转、如何用for await...of、it-pipe、it-all、it-to-buffer等工具重写各类流式与缓冲式调用以及如何用globSource/urlSource替代被删除的addFromFs/addFromURL。本文所有代码示例均来自官方迁移指南并补充了当前仓库源码中的类型定义与底层实现佐证。迁移背景0.48.0 核心 API 为何而变在 0.48.0 之前js-ipfs 的核心 API 同时背负了三套异步模型回调callback风格ipfs.id((err, res) { ... })两套流式实现Pull Streamsipfs.catPullStream()与 Node.js Streamsipfs.catReadableStream()缓冲式 APIipfs.add、ipfs.cat、ipfs.ls等默认把全部结果收进内存后才返回。从 0.48.0 起新核心 API 统一收敛为Promise Async Iterables。从当前仓库的类型定义可以直观看到这个设计packages/ipfs-core-types/src/root.ts 中add: (entry, options) PromiseAddResult—— 单文件/数据导入直接返回 PromiseaddAll: (source, options) AsyncIterableAddResult—— 批量导入返回异步可迭代对象边导入边产出结果cat: (ipfsPath, options) AsyncIterableUint8Array—— 读取文件内容按块chunk产出Uint8Arrayls: (ipfsPath, options) AsyncIterableIPFSEntry—— 目录列表逐条产出条目。也就是说返回 Promise 与 返回 AsyncIterable 成为新 API 的两个基本形态单结果操作返回 Promise多结果/流式操作返回 AsyncIterable。官方迁移指南为每个迁移场景标注了影响等级本文沿用这一标记标记含义容易 —— 应用代码只需简单重构中等 —— 应用代码需要一定规模的重构困难 —— 应用代码需要较复杂的重构从回调迁移两条可行路径新 API 不再支持回调。如果你的应用以回调为主官方给出两种主要迁移方案。方案一难度 切换到 Promise API async/await程序延续不再发生在回调里而是发生在异步调用之后发起调用的函数需要改为async函数。// 迁移前回调风格 function main () { ipfs.id((err, res) { console.log(res) }) } main()// 迁移后async/await 风格 async function main () { const res await ipfs.id() console.log(res) } main()注意这里的ipfs.id()返回的是 Promiseres即节点身份信息对象。从仓库实现看packages/ipfs-core/src/components/id.js 中的id组件本身就是async function并通过withTimeoutOption包装L79支持传入timeout等选项——这正是新 API 是 Promise在实现层面的体现。方案二难度 用callbackify把 Promise API 转回回调 API如果你希望在过渡期内继续使用回调可以借助一个工具模块将 Promise API 转换回回调 API。// 迁移前回调风格 function main () { ipfs.id((err, res) { console.log(res) }) } main()// 迁移后用 callbackify 包装 Promise API const callbackify require(callbackify) const ipfsId callbackify(ipfs.id) async function main () { ipfsId((err, res) { console.log(res) }) } main()这相当于为单个方法如ipfs.id生成了一个回调风格的薄包装适合逐步迁移、逐方法切换的过渡场景。从PeerId迁移peer ID 字符串也是 CIDLibp2p 的PeerId实例不再由 API 直接返回。如果你的应用依赖PeerId的加密能力需要把新 API 返回的 peer ID字符串重新转换为 libp2pPeerId实例。难度 peer ID 字符串本身就是 CID因此转换非常简单const peerId PeerId.createFromB58String(peerIdStr)PeerId类可以通过 npm 引入或在浏览器中以 script 标签方式加载后从全局对象获取// npm / 模块方式 import { PeerId } from libp2p/interface-peer-id const peerId PeerId.createFromB58String(peerIdStr)!-- 浏览器 script 方式 -- script src.../peer-id/dist/index.min.js/script script const peerId window.PeerId.createFromB58String(peerIdStr) /script从当前仓库看新 API 确实是以字符串/CID 形式承载 peer 身份的例如 packages/ipfs-core/src/components/id.js 中会把peer.id.toString()写入返回对象的id字段并在组装addresses时拼接/p2p/${idStr}后缀L63-L70而libp2p/interface-peer-id包在整个仓库的 CLI、核心组件与接口测试中被广泛引用如 packages/ipfs-cli/src/commands/id.js、packages/ipfs-core/src/components/libp2p.js说明它正是当前项目使用的 peer ID 类型来源。从PeerInfo迁移普通对象取代实例Libp2p 的PeerInfo实例同样不再由 API 返回取而代之的是形如{ id: string, addrs: Multiaddr[] }的普通对象。要把这种普通对象转回PeerInfo实例难度 实例化一个新的PeerInfo并把地址逐一加入const peerInfo new PeerInfo(PeerId.createFromB58String(info.id)) info.addrs.forEach(addr peerInfo.multiaddrs.add(addr))同样PeerInfo与PeerId类都可以通过 npm 或 script 标签获取const PeerInfo require(peer-info) import { PeerId } from libp2p/interface-peer-id const peerInfo new PeerInfo(PeerId.createFromB58String(info.id)) info.addrs.forEach(addr peerInfo.multiaddrs.add(addr))script src.../peer-info/dist/index.min.js/script script src.../peer-id/dist/index.min.js/script script const peerInfo new window.PeerInfo(window.PeerId.createFromB58String(info.id)) info.addrs.forEach(addr peerInfo.multiaddrs.add(addr)) /script迁移到 Async Iterables核心概念与工具生态Async Iterables 是 JavaScript 语言原生支持的流式数据方案。此前核心 API 同时支持 Pull Streams 与 Node.js Streams 两套流实现与它们类似异步可迭代对象也按用途分为四种形态source—— 可被消费的源头。类比 Pull Streams 的 source 或 Node.js 的 readable 流sink—— 消费抽干source 的东西。类比 Pull Streams 的 sink 或 Node.js 的 writable 流transform—— 既是 sink 又是 source它消费的值与可被消费的值以某种方式关联。类比两套流体系中的 transformduplex—— 类似 transform但它消费的值与可被消费的值不一定有关联。围绕 async iterables 生态官方推荐了一批实用模块it-pipe流水线、it-all收集全部结果、it-last取最后一个结果、it-concat拼接字节、it-to-buffer转为 Buffer、toStream即it-to-streamasync iterable 与 Node.js stream 互转、stream-to-itNode.js stream 转 async iterable、pull-stream-to-async-iteratorpull stream 转 async iterable、async-iterator-to-pull-streamasync iterable 转 pull stream等。后续章节的每个示例都会用到其中的某些工具。从 Node.js Streams 迁移Node.js Readable Streams*ReadableStream方法被移除现代 Node.js 的 readable 流本身就是 async iterable因此凡是把流传给 API的用法无需改动。被移除的是*ReadableStream系列方法如ipfs.catReadableStream。迁移有两种选择。方案一难度 用for await...of循环消费 async iterable// 迁移前catReadableStream 事件监听 const readable ipfs.catReadableStream(QmHash) const decoder new TextDecoder() readable.on(data, chunk { console.log(decoder.decode(chunk)) }) readable.on(end, () { console.log(done) })// 迁移后ipfs.cat 返回 async iterable逐块消费 const source ipfs.cat(QmHash) const decoder new TextDecoder() for await (const chunk of source) { console.log(decoder.decode(chunk)) } console.log(done)方案二难度 把 async iterable 转回 readable stream// 迁移前catReadableStream 事件监听 const readable ipfs.catReadableStream(QmHash) const decoder new TextDecoder() readable.on(data, chunk { console.log(decoder.decode(chunk)) }) readable.on(end, () { console.log(done) })// 迁移后用 it-to-stream 的 readable 转换 import toStream from it-to-stream const readable toStream.readable(ipfs.cat(QmHash)) const decoder new TextDecoder() readable.on(data, chunk { console.log(decoder.decode(chunk)) }) readable.on(end, () { console.log(done) })从源码看HTTP 客户端的cat实现本身就是基于 async generator 逐块转发响应体的packages/ipfs-http-client/src/cat.js 中async function * cat通过yield * res.iterator()把 HTTP 响应体流式地产出给调用方——这正是ipfs.cat(QmHash)可以直接被for await消费的实现基础。Piping Node.js Streams.pipe/pipeline很多应用会用.pipe或 Node.js 的pipeline把多个流串起来。迁移也有两条路。方案一难度 用it-pipefor await拼接数据// 迁移前pipeline(catReadableStream, concatWritable) const { pipeline, Writable } require(stream) const decoder new TextDecoder() let data new Uint8Array(0) const concat new Writable({ write (chunk, enc, cb) { data uint8ArrayConcat([data, chunk]) cb() } }) pipeline( ipfs.catReadableStream(QmHash), concat, err { console.log(decoder.decode(chunk)) } )// 迁移后it-pipe 流水线 async sink const pipe require(it-pipe) const decoder new TextDecoder() let data new Uint8Array(0) const concat async source { for await (const chunk of source) { data uint8ArrayConcat([data, chunk]) } } const data await pipe( ipfs.cat(QmHash), concat ) console.log(decoder.decode(data))顺带一提上面这段代码还可以更简洁地用it-to-buffer一步完成import toBuffer from it-to-buffer const decoder new TextDecoder() const data await toBuffer(ipfs.cat(QmHash)) console.log(decoder.decode(data))方案二难度 把 async iterable 转回 readable stream 再走原 pipeline// 迁移前pipeline(catReadableStream, concatWritable) const { pipeline, Writable } require(stream) const decoder new TextDecoder() let data new Uint8Array(0) const concat new Writable({ write (chunk, enc, cb) { data uint8ArrayConcat([data, chunk]) cb() } }) pipeline( ipfs.catReadableStream(QmHash), concat, err { console.log(decoder.decode(data)) } )// 迁移后先 toStream.readable 转换再 pipeline import toStream from it-to-stream const { pipeline, Writable } require(stream) const decoder new TextDecoder() let data new Uint8Array(0) const concat new Writable({ write (chunk, enc, cb) { data uint8ArrayConcat([data, chunk]) cb() } }) pipeline( toStream.readable(ipfs.cat(QmHash)), concat, err { console.log(decoder.decode(data)) } )Node.js Transform Streams文件系统流 → IPFS 添加常见的场景是从文件系统读取一个文件的 readable 流然后添加到 IPFS。迁移也有两条路。方案一难度 用it-pipefor await收集全部结果// 迁移前pipeline(fs.readStream, addReadableStream, allWritable) import fs from fs const { pipeline } require(stream) const items [] const all new Writable({ objectMode: true, write (chunk, enc, cb) { items.push(chunk) cb() } }) pipeline( fs.createReadStream(/path/to/file), ipfs.addReadableStream(), all, err { console.log(items) } )// 迁移后it-pipefs 流本身就是 iterable直接进入流水线 import fs from fs const pipe require(it-pipe) const items [] const all async source { for await (const chunk of source) { items.push(chunk) } } await pipe( fs.createReadStream(/path/to/file), // Because Node.js streams are iterable ipfs.add, all ) console.log(items)同样可以更简洁用it-all替代手写的收集 sinkimport fs from fs const pipe require(it-pipe) import all from it-all const items await pipe( fs.createReadStream(/path/to/file), ipfs.add, all ) console.log(items)方案二难度 用it-to-stream的 transform 转换后保留原 pipeline// 迁移前pipeline(fs.readStream, addReadableStream, allWritable) import fs from fs const { pipeline } require(stream) const items [] const all new Writable({ objectMode: true, write (chunk, enc, cb) { items.push(chunk) cb() } }) pipeline( fs.createReadStream(/path/to/file), ipfs.addReadableStream(), all, err { console.log(items) } )// 迁移后toStream.transform(ipfs.add) import toStream from it-to-stream import fs from fs const { pipeline } require(stream) const items [] const all new Writable({ objectMode: true, write (chunk, enc, cb) { items.push(chunk) cb() } }) pipeline( fs.createReadStream(/path/to/file), toStream.transform(ipfs.add), all, err { console.log(items) } )这里ipfs.add之所以能作为 transform 段参与流水线是因为核心实现里add本身就是消费一个输入流、产出添加结果流的形态在 packages/ipfs-core/src/components/add.js 中add由addAll组合而来——last(addAll(normaliseInput(entry), options))而 packages/ipfs-core/src/components/add-all/index.js 中的addAll正是用it-pipe把normaliseInput(source) → importer → transformFile → preloadFile → pinFile串成一条 async generator 流水线逐条yield添加结果。HTTP 客户端一侧的add同样是addAllit-last的组合packages/ipfs-http-client/src/add.js而addAll通过 multipart 请求上传并把响应体的 NDJSON 逐行解析、流式产出packages/ipfs-http-client/src/add-all.js。从 Pull Streams 迁移Pull Streams 不能再直接传给 API 方法*PullStream系列方法如ipfs.catPullStream、ipfs.addPullStream已被移除。Source Pull Streams如果要把一个 pull stream 直接传给 API 方法需先用pull-stream-to-async-iterator把它转成 async iterable。如果是从*PullStream方法迁移则有两条路。方案一难度 用for await消费 async iterable// 迁移前pull(catPullStream, through, onEnd) const decoder new TextDecoder() pull( ipfs.catPullStream(QmHash), pull.through(chunk { console.log(decoder.decode(data)) }), pull.onEnd(err { console.log(done) }) )// 迁移后for await 直接消费 ipfs.cat 返回的 async iterable const decoder new TextDecoder() for await (const chunk of ipfs.cat(QmHash)) { console.log(decoder.decode(data)) } console.log(done)方案二难度 把 async iterable 转回 pull stream// 迁移前pull(catPullStream, through, onEnd) const decoder new TextDecoder() pull( ipfs.catPullStream(QmHash), pull.through(chunk { console.log(decoder.decode(data)) }), pull.onEnd(err { console.log(done) }) )// 迁移后async-iterator-to-pull-stream 的 source 转换 const toPull require(async-iterator-to-pull-stream) const decoder new TextDecoder() pull( toPull.source(ipfs.cat(QmHash)), pull.through(chunk { console.log(decoder.decode(data)) }), pull.onEnd(err { console.log(done) }) )Pull Stream Pipelinespull()串联许多应用习惯用pull()把 pull streams 串成管道。难度 用it-pipeit-concat拼接数据// 迁移前pull(catPullStream, collect) const decoder new TextDecoder() pull( ipfs.catPullStream(QmHash), pull.collect((err, chunks) { console.log(decoder.decode(uint8ArrayConcat(chunks))) }) )// 迁移后it-pipe it-concat const pipe require(it-pipe) import concat from it-concat const decoder new TextDecoder() const data await pipe( ipfs.cat(QmHash), concat ) console.log(decoder.decode(data))Transform Pull Streamspull stream 文件 → IPFS 添加你可能有一个来自文件系统的 pull stream 源文件想添加到 IPFS。同样两条路。方案一难度 用it-pipeit-all收集全部结果// 迁移前pull(streamToPull.source(fs.readStream), addPullStream, collect) import fs from fs const toPull require(stream-to-pull-stream) pull( toPull.source(fs.createReadStream(/path/to/file)), ipfs.addPullStream(), pull.collect((err, items) { console.log(items) }) )// 迁移后单个文件直接 await ipfs.add import fs from fs const file await ipfs.add(fs.createReadStream(/path/to/file)) console.log(file)方案二难度 把 async iterable 转回 pull stream// 迁移前pull(streamToPull.source(fs.readStream), addPullStream, collect) import fs from fs const toPull require(stream-to-pull-stream) pull( toPull.source(fs.createReadStream(/path/to/file)), ipfs.addPullStream(), pull.collect((err, items) { console.log(items) }) )// 迁移后async-iterator-to-pull-stream 的 transform 转换 import fs from fs const streamToPull require(stream-to-pull-stream) const itToPull require(async-iterator-to-pull-stream) pull( streamToPull.source(fs.createReadStream(/path/to/file)), itToPull.transform(ipfs.add), pull.collect((err, items) { console.log(items) }) )从缓冲式 API 迁移add/cat/ls旧的ipfs.add、ipfs.cat、ipfs.ls是缓冲式API——它们先把全部结果收集进内存再返回。新接口默认流式返回目的在于降低内存占用、缩短首字节时间time to first byte、提供更及时的反馈。注意在新 API 中add/cat/ls的语义已经改变——add返回 Promise单文件场景最常用addAll/cat/ls返回 AsyncIterable。下面给出官方示例。添加文件难度 // 迁移前addAll 返回完整结果数组全部文件已添加完成 const results await ipfs.addAll([ { path: root/1.txt, content: one }, { path: root/2.txt, content: two } ]) // Note that ALL files have already been added to IPFS results.forEach(file { console.log(file.path) })// 迁移后addAll 返回 async iterable边添加边产出 const addSource ipfs.addAll([ { path: root/1.txt, content: one }, { path: root/2.txt, content: two } ]) for await (const file of addSource) { console.log(file.path) // Note these are logged out as they are added }如果确实需要把结果缓冲成数组用it-all工具import all from it-all const results await all(ipfs.addAll([ { path: root/1.txt, content: one }, { path: root/2.txt, content: two } ])) results.forEach(file { console.log(file.path) })批量添加时你通常只关心最后一个结果即根目录条目。旧代码是取数组末元素const results await ipfs.addAll([ { path: root/1.txt, content: one }, { path: root/2.txt, content: two } ]) const lastResult results[results.length - 1] console.log(lastResult)新写法是遍历时记录最后一个const addSource ipfs.addAll([ { path: root/1.txt, content: one }, { path: root/2.txt, content: two } ]) let lastResult for await (const file of addSource) { lastResult file } console.log(lastResult)或用it-last工具一步到位const lastResult await last(ipfs.addAll([ { path: root/1.txt, content: one }, { path: root/2.txt, content: two } ])) console.log(lastResult)读取文件难度 // 迁移前cat 返回完整文件内容一次性读入内存 import fs from fs const data await ipfs.cat(/ipfs/QmHash) // Note that here we have read the entire file // i.e. data holds ALL the contents of the file in memory await fs.writeFile(/tmp/file.iso, data) console.log(done)// 迁移后边到达边写入内存可及时释放复用 const pipe require(it-pipe) import toIterable from stream-to-it import fs from fs // Note that as chunks arrive they are written to the file and memory can be freed and re-used await pipe( ipfs.cat(/ipfs/QmHash), toIterable.sink(fs.createWriteStream(/tmp/file.iso)) ) console.log(done)如果你确实想缓冲全部块也可以用it-concat官方明确不推荐会重新引入内存压力import fs from fs import concat from it-concat const data await concat(ipfs.cat(/ipfs/QmHash)) await fs.writeFile(/tmp/file.iso, data.slice()) console.log(done)列目录难度 // 迁移前ls 返回完整列表整个目录已被读入内存 const files await ipfs.ls(/ipfs/QmHash) // Note that ALL files in the directory have been read into memory files.forEach(file { console.log(file.name) })// 迁移后ls 返回 async iterable逐条产出 const filesSource ipfs.ls(/ipfs/QmHash) for await (const file of filesSource) { console.log(file.name) // Note these are logged out as they are retrieved from the network/disk }同样可以借助it-all缓冲import all from it-all const results await all(ipfs.ls(/ipfs/QmHash)) results.forEach(file { console.log(file.name) })从addFromFs迁移改用globSourceaddFromFs方法已被移除取而代之的是从js-ipfs/js-ipfs-http-client导出的辅助函数globSource。难度 // 迁移前addFromFs 递归添加目录 const IpfsHttpClient require(ipfs-http-client) const ipfs IpfsHttpClient() const files await ipfs.addFromFs(./docs, { recursive: true }) files.forEach(file { console.log(file) })// 迁移后globSource 生成输入流交给 addAll 流式消费 const IpfsHttpClient require(ipfs-http-client) const { globSource } IpfsHttpClient const ipfs IpfsHttpClient() for await (const file of ipfs.addAll(globSource(./docs, { recursive: true }))) { console.log(file) }globSource(path, pattern, options)的职责是把磁盘上的文件按 glob 模式匹配转换为addAll可直接消费的输入流。从仓库实现看globSource与urlSource确实由 HTTP 客户端显式对外导出在 packages/ipfs-http-client/src/index.js 中导入ipfs-utils/src/files/glob-source.js并在文件末尾export const globSource globSourceImportL153、export { default as urlSource } from ipfs-utils/src/files/url-source.jsL152。此外接口测试套件也大量使用该组合例如 packages/interface-ipfs-core/src/add-all.js 中通过all(ipfs.addAll(globSource(filesPath, **/*)))验证递归添加、通过globSource(filesPath, (!(files*)))验证 glob 排除规则、并通过{ hidden: true }选项验证隐藏文件处理L448——这些测试直接印证了globSource的参数形态与行为。从addFromURL迁移改用urlSourceaddFromURL方法已被移除取而代之的是同样从js-ipfs/js-ipfs-http-client导出的辅助函数urlSource。难度 // 迁移前addFromURL 下载 URL 内容并添加 const IpfsHttpClient require(ipfs-http-client) const ipfs IpfsHttpClient() const files await ipfs.addFromURL(https://ipfs.io/images/ipfs-logo.svg) files.forEach(file { console.log(file) })// 迁移后urlSource 生成输入流交给 ipfs.add const IpfsHttpClient require(ipfs-http-client) const { urlSource } IpfsHttpClient const ipfs IpfsHttpClient() const file await ipfs.add(urlSource(https://ipfs.io/images/ipfs-logo.svg)) console.log(file)注意这里urlSource(...)返回的是一个输入候选import candidate因此可以传给ipfs.add返回 Promise或放进ipfs.addAll的输入流中示例中使用ipfs.add并直接await拿到单个文件结果。从addFromStream迁移直接用addaddFromStream方法已被移除——它原本只是add的别名因此迁移最为直接。难度 // 迁移前addFromStream(fs.createReadStream(...)) const IpfsHttpClient require(ipfs-http-client) const ipfs IpfsHttpClient() const files await ipfs.addFromStream(fs.createReadStream(/path/to/file.txt)) files.forEach(file { console.log(file) })// 迁移后ipfs.add 直接接受 Node.js 流流本身是 async iterable import fs from fs const ipfs IpfsHttpClient() const file await ipfs.add(fs.createReadStream(/path/to/file.txt)) console.log(file)之所以ipfs.add能直接接收fs.createReadStream()的输出是因为核心输入规范化逻辑对多种输入形态做了统一处理在 packages/ipfs-core-utils/src/files/normalise-candidate-single.js 中字符串、Uint8Array/ArrayBuffer/TypedArray、Blob/File、浏览器ReadableStream、Node.js 可读流具有Symbol.asyncIterator、{ path, content }文件对象都会被归一为统一的输入流甚至当传入一个包含多个元素的迭代器时会明确抛出请改用ipfs.addAll的错误提示L73——这与本文前面单结果用add、多结果用addAll的边界完全一致。迁移自查清单完成迁移后建议对照以下要点自查回调已全部移除代码中不再出现ipfs.xxx((err, res) ...)形态需要回调过渡的地方已用callbackify显式包装。流式方法名已更新*ReadableStream、*PullStream系列方法已全部替换为ipfs.cat、ipfs.add、ipfs.addAll、ipfs.ls等新形态。结果消费方式正确返回 Promise 的方法add、id、version等用await返回 AsyncIterable 的方法addAll、cat、ls等用for await...of或it-all/it-last/it-concat/it-to-buffer等工具。peer 数据结构已适配API 返回的是 peer ID 字符串与{ id, addrs }普通对象需要 libp2p 能力时再转换为PeerId/PeerInfo实例。辅助函数已就位目录添加用globSourceaddAllURL 添加用urlSourceadd文件流添加直接用add。输入形态符合新 APINode.js 流、pull stream先转 async iterable、字符串、字节数组、{ path, content }对象均可作为输入注意单个输入用add多个输入用addAll。补充说明本文所有迁移示例与影响等级均来自官方迁移指南 docs/MIGRATION-TO-ASYNC-AWAIT.md源码佐证来自当前仓库的 packages/ipfs-core-types/src/root.ts类型定义、packages/ipfs-core/src/components/add-all/index.js 与 packages/ipfs-core/src/components/add.js核心实现、packages/ipfs-http-client/src/index.jsglobSource/urlSource导出、packages/ipfs-http-client/src/add-all.js 与 packages/ipfs-http-client/src/cat.jsHTTP 客户端流式实现、packages/ipfs-core-utils/src/files/normalise-candidate-single.js输入规范化、以及 packages/interface-ipfs-core/src/add-all.jsglobSource接口测试。it-pipe、it-all、it-last、it-concat、it-to-buffer、it-to-stream、stream-to-it、pull-stream-to-async-iterator、async-iterator-to-pull-stream等为独立工具模块需按需安装async iterables 生态的更多辅助函数可关注 TC39 的 iterator helpers 提案进展。本文涉及的版本行为0.48.0 起的 API 变更以本仓库当前内容为准不同版本之间 API 形态可能存在差异升级前请核对目标版本的 CHANGELOG.md 与 docs/core-api 文档。赞分享存储网络通信【免费下载链接】js-ipfsIPFS implementation in JavaScript项目地址https://gitcode.com/gh_mirrors/js/js-ipfs点击查看免费下载相关推荐Egg 2.x 升级指南从 co/generator 全面迁移到 async/awaitEgg 2.x 升级指南从 co/generator 全面迁移到 async/await 本指南以 Egg 官方升级文档 site/docs/intro/mi后端Web框架Meteor 2.12 迁移指南用 WARN_WHEN_USING_OLD_API 定位旧版同步 API为 async/await 迁移铺路Meteor 2.12 迁移指南用 WARN_WHEN_USING_OLD_API 定位旧版同步 API为 async/await 迁移铺路 本指南面向正在后端前端开发工具移动开发Egg 框架 async function 开发指南从 generator 到 async/await 的完整迁移实践Egg 框架 async function 开发指南从 generator 到 async/await 的完整迁移实践 本指南以 Egg当前仓库 eggjs后端Web框架上一篇StickySwitch高级定制教程创建独特动画效果和主题风格下一篇FE-Interview中的CSS Houdini自定义渲染API创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考