This question is the exact reverse of converting a ReadableStream into a ReadStream.
ReadStream
is a structure used in Node.jsReadableStream
is a structure brought by the web platform
With the advent of non-Node.js runtimes such as Deno or the "Edge runtime" in Next.js, it can be useful to convert a Node.js specific ReadStream
into a generic ReadableStream
.
This is for instance useful to send files from a Next.js route handler, see this discussion on Next.js GitHub.
I've drafted a piece of code like so:
const downloadStream = fs.createReadStream(zipFilePath);
const readStream = new ReadableStream({
start(controller) {
return pump();
function pump() {
return downloadStream.read().then(({ done, value }) => {
// When no more data needs to be consumed, close the stream
if (done) {
controller.close();
return;
}
// Enqueue the next data chunk into our target stream
controller.enqueue(value);
return pump();
});
}
},
});
I am in the process of testing it.
Edit: the problem with this first draft is that stream.Readable
read()
method doesn't return a promise, as mentioned by @Mahesh in the comments.
Here is a second try:
const downloadStream = fs.createReadStream(zipFilePath);
const readStream = new ReadableStream({
start(controller) {
return pump();
function pump() {
const buf = downloadStream.read() as Buffer
if (buf === null) {
controller.close();
return;
}
controller.enqueue(buf.toString());
return pump();
}
},
});
It gives me a null buffer immediately despite the file weighing 344 bytes. When I call isPaused()
, the stream doesn't seem to be paused. Calling pause()
doesn't fix my issue, neither adding an explicit size of 1 byte to read()
.
I also get a weird error from Next.js:
- error Error: aborted
at connResetException (node:internal/errors:711:14)
at Socket.socketCloseListener (node:_http_client:454:19)
at Socket.emit (node:events:525:35)
at TCP.<anonymous> (node:net:313:12) {
code: 'ECONNRESET'
}
Are there simpler solutions, syntax-wise?