37

I have the url to a possibly large (100+ Mb) file, how do I save it in a local directory using fetch?

I looked around but there don't seem to be a lot of resources/tutorials on how to do this.

Thank you!

VLAZ
  • 22,934
  • 9
  • 44
  • 60
Gloomy
  • 740
  • 1
  • 7
  • 18

3 Answers3

66

Using the Fetch API you could write a function that could download from a URL like this:

const downloadFile = (async (url, path) => {
  const res = await fetch(url);
  const fileStream = fs.createWriteStream(path);
  await new Promise((resolve, reject) => {
      res.body.pipe(fileStream);
      res.body.on("error", reject);
      fileStream.on("finish", resolve);
    });
});
machineghost
  • 30,831
  • 28
  • 141
  • 212
code_wrangler
  • 944
  • 6
  • 9
21

If you want to avoid explicitly making a Promise like in the other very fine answer, and are ok with building a buffer of the entire 100+ MB file, then you could do something simpler:

const fetch = require('node-fetch');
const {writeFile} = require('fs');
const {promisify} = require('util');
const writeFilePromise = promisify(writeFile);

function downloadFile(url, outputPath) {
  return fetch(url)
      .then(x => x.arrayBuffer())
      .then(x => writeFilePromise(outputPath, Buffer.from(x)));
}

But the other answer will be more memory-efficient since it's piping the received data stream directly into a file without accumulating all of it in a Buffer.

Ahmed Fasih
  • 6,421
  • 5
  • 50
  • 92
  • I have tried this code but got error...I got error [Error: EISDIR: illegal operation on a directory, open 'D:\Work\repo\'] { errno: -4068, code: 'EISDIR', syscall: 'open', path: 'D:\\Work\\repo\\' } – Scott Jones May 23 '22 at 09:08
  • @ScottJones `EISDIR` means "Error: IS Directory": you're giving Node a directory when it expects a file. Just use `d:\work\repo\file.txt` for example – Ahmed Fasih May 23 '22 at 16:45
6
const {createWriteStream} = require('fs');
const {pipeline} = require('stream/promises');
const fetch = require('node-fetch');

const downloadFile = async (url, path) => pipeline(
    (await fetch(url)).body,
    createWriteStream(path)
);
Ihor Sakailiuk
  • 4,714
  • 3
  • 22
  • 26
  • I get error `TypeError: Cannot read property 'on' of undefined at destroyer (internal/streams/pipeline.js:23:10)` – Codler Oct 17 '20 at 07:35