我正在创建一个 Electron 应用程序,我想将图像流式传输到文件(所以基本上是下载它)。
我想使用原生的Fetch API,因为请求模块会是一个很大的开销。
但是响应中没有管道方法,所以我不能做类似的事情
fetch('https://imageurl.jpg')
.then(response => response.pipe(fs.createWriteStream('image.jpg')));
那么我怎样才能将
fetch
和fs.createWriteStream
结合起来呢?
2023 年更新
现在 Node.JS 为此目的提供了助手。查看 frank-dspeed 的回答 了解更多详情。
原答案
我成功了。我创建了一个将响应转换为可读流的函数。
const responseToReadable = response => {
const reader = response.body.getReader();
const rs = new Readable();
rs._read = async () => {
const result = await reader.read();
if(!result.done){
rs.push(Buffer.from(result.value));
}else{
rs.push(null);
return;
}
};
return rs;
};
有了它,我就能做到
fetch('https://imageurl.jpg')
.then(response => responseToReadable(response).pipe(fs.createWriteStream('image.jpg')));
我想今天的答案是 Nodejs 18+
node -e 'fetch("https://github.com/stealify").then(response => stream.Readable.fromWeb(response.body).pipe(fs.createWriteStream("./github.com_stealify.html")))'
在上面的示例中,我们使用 -e 标志,它告诉 nodejs 执行我们的 cli 代码,我们在这里下载一个有趣的项目页面并将其另存为 ./github.com_stealify.html 在当前工作目录中,下面的代码显示了相同的内容为了方便起见,放在 nodejs .mjs 文件中
使用 CommonJS 的 Cli 示例
node -e 'fetch("https://github.com/stealify").then(({body:s}) =>
stream.Readable.fromWeb(s).pipe(fs.createWriteStream("./github.com_stealify.html")))'
fetch.cjs
fetch("https://github.com/stealify").then(({body:s}) =>
require("node:stream").Readable.fromWeb(s)
.pipe(require("node:fs").createWriteStream("./github.com_stealify.html")));
使用 ESM 的 CLI 示例
node --input-type module -e 'stream.Readable.fromWeb(
(await fetch("https://github.com/stealify")).body)
.pipe(fs.createWriteStream("./github.com_stealify.html"))'
fetch_tla_no_tli.mjs
(await import("node:stream")).Readable.fromWeb(
(await fetch("https://github.com/stealify")).body).pipe(
(await import("node:fs")).createWriteStream("./github.com_stealify.html"));
fetch.mjs
import stream from 'node:stream';
import fs from 'node:fs';
stream.Readable
.fromWeb((await fetch("https://github.com/stealify")).body)
.pipe(fs.createWriteStream("./github.com_stealify.html"));
参见:https://nodejs.org/api/stream.html#streamreadablefromwebreadablestream-options
这是正确的用法,因为 fs.promises 支持与流/消费者 api 相同的所有形式的迭代器
node -e 'fetch("https://github.com/stealify").then( (res) =>
fs.promises.writeFile("./github.com_stealify.html", res.body)))'
对于 TypeScript:
import { Readable } from "stream";
import fs from 'fs'
import { ReadableStream } from "stream/web";
fetch("https://github.com/stealify").then((res) => {
if (res.body) {
return Readable.fromWeb(res.body as ReadableStream<any>)
.pipe(fs.createWriteStream("./tmp/github.com_stealify.html"))
}
})
Fetch 并不真正能够开箱即用地使用 Nodejs Streams,因为浏览器中的 Stream API 与 NodeJS 提供的 API 不同,即您不能将浏览器流传输到 NodeJS 流中,反之亦然。
Electron-fetch 模块似乎可以为您解决这个问题。或者你可以看看这个答案:https://stackoverflow.com/a/32545850/2016129有一种无需nodeIntegration即可下载文件的方法。
还有