How to download a file with Node.js (without using third-party libraries)?

后端 未结 28 1279
逝去的感伤
逝去的感伤 2020-11-22 03:37

How do I download a file with Node.js without using third-party libraries?

I don\'t need anything special. I only want to download a file from a giv

相关标签:
28条回答
  • 2020-11-22 04:12

    Speaking of handling errors, it's even better listening to request errors too. I'd even validate by checking response code. Here it's considered success only for 200 response code, but other codes might be good.

    const fs = require('fs');
    const http = require('http');
    
    const download = (url, dest, cb) => {
        const file = fs.createWriteStream(dest);
    
        const request = http.get(url, (response) => {
            // check if response is success
            if (response.statusCode !== 200) {
                return cb('Response status was ' + response.statusCode);
            }
    
            response.pipe(file);
        });
    
        // close() is async, call cb after close completes
        file.on('finish', () => file.close(cb));
    
        // check for request error too
        request.on('error', (err) => {
            fs.unlink(dest);
            return cb(err.message);
        });
    
        file.on('error', (err) => { // Handle errors
            fs.unlink(dest); // Delete the file async. (But we don't check the result) 
            return cb(err.message);
        });
    };
    

    Despite the relative simplicity of this code, I would advise to use the request module as it handles many more protocols (hello HTTPS!) which aren't natively supported by http.

    That would be done like so:

    const fs = require('fs');
    const request = require('request');
    
    const download = (url, dest, cb) => {
        const file = fs.createWriteStream(dest);
        const sendReq = request.get(url);
    
        // verify response code
        sendReq.on('response', (response) => {
            if (response.statusCode !== 200) {
                return cb('Response status was ' + response.statusCode);
            }
    
            sendReq.pipe(file);
        });
    
        // close() is async, call cb after close completes
        file.on('finish', () => file.close(cb));
    
        // check for request errors
        sendReq.on('error', (err) => {
            fs.unlink(dest);
            return cb(err.message);
        });
    
        file.on('error', (err) => { // Handle errors
            fs.unlink(dest); // Delete the file async. (But we don't check the result)
            return cb(err.message);
        });
    };
    
    0 讨论(0)
  • 2020-11-22 04:12

    for those who came in search of es6-style promise based way, I guess it would be something like:

    var http = require('http');
    var fs = require('fs');
    
    function pDownload(url, dest){
      var file = fs.createWriteStream(dest);
      return new Promise((resolve, reject) => {
        var responseSent = false; // flag to make sure that response is sent only once.
        http.get(url, response => {
          response.pipe(file);
          file.on('finish', () =>{
            file.close(() => {
              if(responseSent)  return;
              responseSent = true;
              resolve();
            });
          });
        }).on('error', err => {
            if(responseSent)  return;
            responseSent = true;
            reject(err);
        });
      });
    }
    
    //example
    pDownload(url, fileLocation)
      .then( ()=> console.log('downloaded file no issues...'))
      .catch( e => console.error('error while downloading', e));
    
    0 讨论(0)
  • 2020-11-22 04:12

    You can use https://github.com/douzi8/ajax-request#download

    request.download('http://res.m.ctrip.com/html5/Content/images/57.png', 
      function(err, res, body) {}
    );
    
    0 讨论(0)
  • 2020-11-22 04:13

    Maybe node.js has changed, but it seems there are some problems with the other solutions (using node v8.1.2):

    1. You don't need to call file.close() in the finish event. Per default the fs.createWriteStream is set to autoClose: https://nodejs.org/api/fs.html#fs_fs_createwritestream_path_options
    2. file.close() should be called on error. Maybe this is not needed when the file is deleted (unlink()), but normally it is: https://nodejs.org/api/stream.html#stream_readable_pipe_destination_options
    3. Temp file is not deleted on statusCode !== 200
    4. fs.unlink() without a callback is deprecated (outputs warning)
    5. If dest file exists; it is overridden

    Below is a modified solution (using ES6 and promises) which handles these problems.

    const http = require("http");
    const fs = require("fs");
    
    function download(url, dest) {
        return new Promise((resolve, reject) => {
            const file = fs.createWriteStream(dest, { flags: "wx" });
    
            const request = http.get(url, response => {
                if (response.statusCode === 200) {
                    response.pipe(file);
                } else {
                    file.close();
                    fs.unlink(dest, () => {}); // Delete temp file
                    reject(`Server responded with ${response.statusCode}: ${response.statusMessage}`);
                }
            });
    
            request.on("error", err => {
                file.close();
                fs.unlink(dest, () => {}); // Delete temp file
                reject(err.message);
            });
    
            file.on("finish", () => {
                resolve();
            });
    
            file.on("error", err => {
                file.close();
    
                if (err.code === "EEXIST") {
                    reject("File already exists");
                } else {
                    fs.unlink(dest, () => {}); // Delete temp file
                    reject(err.message);
                }
            });
        });
    }
    
    0 讨论(0)
  • 2020-11-22 04:14

    Download using promise, which resolve a readable stream. put extra logic to handle the redirect.

    var http = require('http');
    var promise = require('bluebird');
    var url = require('url');
    var fs = require('fs');
    var assert = require('assert');
    
    function download(option) {
        assert(option);
        if (typeof option == 'string') {
            option = url.parse(option);
        }
    
        return new promise(function(resolve, reject) {
            var req = http.request(option, function(res) {
                if (res.statusCode == 200) {
                    resolve(res);
                } else {
                    if (res.statusCode === 301 && res.headers.location) {
                        resolve(download(res.headers.location));
                    } else {
                        reject(res.statusCode);
                    }
                }
            })
            .on('error', function(e) {
                reject(e);
            })
            .end();
        });
    }
    
    download('http://localhost:8080/redirect')
    .then(function(stream) {
        try {
    
            var writeStream = fs.createWriteStream('holyhigh.jpg');
            stream.pipe(writeStream);
    
        } catch(e) {
            console.error(e);
        }
    });
    
    0 讨论(0)
  • 2020-11-22 04:14

    download.js (i.e. /project/utils/download.js)

    const fs = require('fs');
    const request = require('request');
    
    const download = (uri, filename, callback) => {
        request.head(uri, (err, res, body) => {
            console.log('content-type:', res.headers['content-type']);
            console.log('content-length:', res.headers['content-length']);
    
            request(uri).pipe(fs.createWriteStream(filename)).on('close', callback);
        });
    };
    
    module.exports = { download };
    


    app.js

    ... 
    // part of imports
    const { download } = require('./utils/download');
    
    ...
    // add this function wherever
    download('https://imageurl.com', 'imagename.jpg', () => {
      console.log('done')
    });
    
    0 讨论(0)
提交回复
热议问题