javascriptnode.jsstreamnode.js-got

Piping got.stream to a file


I am refactoring some code that was using http module in Node to use got instead. I tried the following:

function get(url, filePath) {
  return new Promise((resolve, reject) => {
    got.stream(url).on
        ("response", response => {
            const newFile = fs.createWriteStream(filePath);
            response.pipe(newFile);
            newFile.on("finish", () => {
              newFile.close(resolve());
            });
            newFile.on("error", err => {
              reject(err);
            });    
        }).on
        ("error", err => {
             reject(err);
        });
  });
}

The finish event never fired. The file (filePath) is created with 0 bytes.

The block of code using newFile was something that worked when I was using the Node http module.

What is the proper way to pipe got.stream to a file?


Solution

  • Per the got() documentation, you want to pipe the stream directly to your file and if you use pipeline() to do it, it will collect errors and report completion.

    const pipeline = promisify(stream.pipeline);
    const fsp = require('fs').promises;
    
    function get(url, filePath) { 
        return pipeline(
            got.stream(url),
            fs.createWriteStream(filePath)
        );
    }
    
    // usage
    get(...).then(() => {
        console.log("all done");
    }).catch(err => {
        console.log(err);
    });
    

    FYI, the point of got.stream() is to return a stream that you can directly use as a stream and since you want it to go to a file, you can pipe that stream to that file. I use pipeline() instead of .pipe() because pipeline has much more complete error handling that .pipe(), though in non-error conditions, .pipe() would also work.


    Here's a version that cleans up the output file if there's an error:

    function get(url, filePath) { 
        return pipeline(
            got.stream(url),
            fs.createWriteStream(filePath)
        ).catch(err => {
             fsp.unlink(filePath).catch(err => {
                 if (err.code !== 'ENOENT') {
                 // trying to delete output file upon error
                     console.log('error trying to delete output file', err);
                 }
             });
             throw err;
        });
    }