14

I try downloading files with the fetch() function from github.
Then i try to save the fetched file Stream as a file with the fs-module.
When doing it, i get this error:

TypeError [ERR_INVALID_ARG_TYPE]: The "transform.writable" property must be an instance of WritableStream. Received an instance of WriteStream

My problem is, that i don't know the difference between WriteStream and WritableStream or how to convert them.

This is the code i run:

async function downloadFile(link, filename = "download") {
    var response = await fetch(link);
    var body = await response.body;
    var filepath = "./" + filename;
    var download_write_stream = fs.createWriteStream(filepath);
    console.log(download_write_stream.writable);
    await body.pipeTo(download_write_stream);
}

Node.js: v18.7.0

0

2 Answers 2

14

Good question. Web streams are something new, and they are different way of handling streams. WritableStream tells us that we can create WritableStreams as follows:

import {
  WritableStream
} from 'node:stream/web';

const stream = new WritableStream({
  write(chunk) {
    console.log(chunk);
  }
});

Then, you could create a custom stream that writes each chunk to disk. An easy way could be:

function getFileWritableStream(filePath) {
  const downloadWriteStream = fs.createWriteStream(filePath);

  /* This adapter is needed because the method .pipeTo() only
  accepts an instance of WritableStream.
  */
  return new WritableStream({
    write: chunk => downloadWriteStream(chunk)
  });
}

async function downloadFile(link, filepath = './download.blob') {
  const response = await fetch(link);
  const body = response.body;
  const fileWritableStream = getFileWritableStream(filepath);
  await body.pipeTo(fileWritableStream);
}
Sign up to request clarification or add additional context in comments.

2 Comments

Do you have an explanation for why you can't body.pipeTo(process.stdout)? Your example of creating a WritableStream and calling process.stdout.write inside works, but why is it necessary? In other places stdin.pipe(stdout) works -- what's different about readable.pipeTo()?
when you wait for response.body, dont it gathers all the filecontents into memory?
11

You can use Readable.fromWeb to convert body, which is a ReadableStream from the web streams API, into a NodeJS Readable stream that can be used with the fs methods.

Note that readable.pipe returns another stream instantly. To wait for it to finish, you can use the promise version of stream.finished to convert it into a Promise, or else you could add listeners for the 'finish' and 'error' events to detect success or failure.

const fs = require('fs');
const { Readable } = require('stream');
const { finished } = require('stream/promises');

async function downloadFile(link, filepath = './download') {
    const response = await fetch(link);
    const body = Readable.fromWeb(response.body);
    const download_write_stream = fs.createWriteStream(filepath);
    await finished(body.pipe(download_write_stream));
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.