首页 文章

如何使用Node.js下载文件(不使用第三方库)?

提问于
浏览
314

如何使用Node.js without using third-party libraries 下载文件?

我不需要任何特别的东西 . 我只想从给定的URL下载文件,然后将其保存到给定的目录中 .

19 回答

  • 438

    您可以创建HTTP GET 请求并将其 response 管道传输到可写文件流中:

    var http = require('http');
    var fs = require('fs');
    
    var file = fs.createWriteStream("file.jpg");
    var request = http.get("http://i3.ytimg.com/vi/J---aiyznGQ/mqdefault.jpg", function(response) {
      response.pipe(file);
    });
    

    如果要支持在命令行上收集信息(如指定目标文件或目录或URL),请查看类似Commander的内容 .

  • 4

    不要忘记处理错误!以下代码基于Augusto Roman的回答 .

    var http = require('http');
    var fs = require('fs');
    
    var download = function(url, dest, cb) {
      var file = fs.createWriteStream(dest);
      var request = http.get(url, function(response) {
        response.pipe(file);
        file.on('finish', function() {
          file.close(cb);  // close() is async, call cb after close completes.
        });
      }).on('error', function(err) { // Handle errors
        fs.unlink(dest); // Delete the file async. (But we don't check the result)
        if (cb) cb(err.message);
      });
    };
    
  • 120

    正如Brandon Tilley所说,但具有适当的控制流程:

    var http = require('http');
    var fs = require('fs');
    
    var download = function(url, dest, cb) {
      var file = fs.createWriteStream(dest);
      var request = http.get(url, function(response) {
        response.pipe(file);
        file.on('finish', function() {
          file.close(cb);
        });
      });
    }
    

    无需等待 finish 事件,天真的脚本可能会以不完整的文件结束 .

    Edit: 感谢@Augusto Roman指出应将 cb 传递给 file.close ,而不是明确调用 .

  • 4

    说到处理错误,它甚至可以更好地监听请求错误 . 我甚至通过检查响应代码来验证 . 这里仅认为200响应代码成功,但其他代码可能是好的 .

    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);
        });
    };
    

    尽管此代码相对简单,但我建议使用request module,因为它处理了更多的协议(hello HTTPS!), http 本身不支持这些协议 .

    这将是这样做的:

    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);
        });
    };
    
  • -1

    gfxmonk的答案在回调和 file.close() 完成之间的数据竞争非常紧张 . file.close() 实际上是在完成关闭时调用的回调 . 否则,立即使用该文件可能会失败(很少!) .

    完整的解决方案是:

    var http = require('http');
    var fs = require('fs');
    
    var download = function(url, dest, cb) {
      var file = fs.createWriteStream(dest);
      var request = http.get(url, function(response) {
        response.pipe(file);
        file.on('finish', function() {
          file.close(cb);  // close() is async, call cb after close completes.
        });
      });
    }
    

    无需等待完成事件,天真的脚本可能会以不完整的文件结束 . 如果不通过关闭安排 cb 回调,您可能会在访问文件和实际准备好的文件之间进行竞争 .

  • 51

    对于那些寻找基于es6风格承诺的方式,我想它会是这样的:

    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

    超时解决方案,防止内存泄漏:

    以下代码基于Brandon Tilley的答案:

    var http = require('http'),
        fs = require('fs');
    
    var request = http.get("http://example12345.com/yourfile.html", function(response) {
        if (response.statusCode === 200) {
            var file = fs.createWriteStream("copy.html");
            response.pipe(file);
        }
        // Add timeout.
        request.setTimeout(12000, function () {
            request.abort();
        });
    });
    

    当你收到错误时不要创建文件,并且在X secondes之后优先使用超时来关闭你的请求 .

  • 13

    也许node.js发生了变化,但似乎其他解决方案存在一些问题(使用节点v8.1.2):

    以下是处理这些问题的修改后的解决方案(使用ES6和承诺) .

    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

    Vince Yuan的代码很棒,但似乎有些不对劲 .

    function download(url, dest, callback) {
        var file = fs.createWriteStream(dest);
        var request = http.get(url, function (response) {
            response.pipe(file);
            file.on('finish', function () {
                file.close(callback); // close() is async, call callback after close completes.
            });
            file.on('error', function (err) {
                fs.unlink(dest); // Delete the file async. (But we don't check the result)
                if (callback)
                    callback(err.message);
            });
        });
    }
    
  • 13
    const download = (url, path) => new Promise((resolve, reject) => {
    http.get(url, response => {
        const statusCode = response.statusCode;
    
        if (statusCode !== 200) {
            return reject('Download error!');
        }
    
        const writeStream = fs.createWriteStream(path);
        response.pipe(writeStream);
    
        writeStream.on('error', () => reject('Error writing to file!'));
        writeStream.on('finish', () => writeStream.close(resolve));
    });}).catch(err => console.error(err));
    
  • 1

    你可以用https://github.com/douzi8/ajax-request#download

    request.download('http://res.m.ctrip.com/html5/Content/images/57.png', 
      function(err, res, body) {}
    );
    
  • 3

    如果您使用快速使用res.download()方法 . 否则fs模块使用 .

    app.get('/read-android', function(req, res) {
       var file = "/home/sony/Documents/docs/Android.apk";
        res.download(file) 
    });
    

    (要么)

    function readApp(req,res) {
          var file = req.fileName,
              filePath = "/home/sony/Documents/docs/";
          fs.exists(filePath, function(exists){
              if (exists) {     
                res.writeHead(200, {
                  "Content-Type": "application/octet-stream",
                  "Content-Disposition" : "attachment; filename=" + file});
                fs.createReadStream(filePath + file).pipe(res);
              } else {
                res.writeHead(400, {"Content-Type": "text/plain"});
                res.end("ERROR File does NOT Exists.ipa");
              }
            });  
        }
    
  • 2

    路径:img类型:jpg随机uniqid

    function resim(url) {
    
        var http = require("http");
        var fs = require("fs");
        var sayi = Math.floor(Math.random()*10000000000);
        var uzanti = ".jpg";
        var file = fs.createWriteStream("img/"+sayi+uzanti);
        var request = http.get(url, function(response) {
      response.pipe(file);
    });
    
            return sayi+uzanti;
    }
    
  • 11

    使用promise下载,解析可读流 . 添加额外的逻辑来处理重定向 .

    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);
        }
    });
    
  • 419
    function download(url, dest, cb) {
    
      var request = http.get(url, function (response) {
    
        const settings = {
          flags: 'w',
          encoding: 'utf8',
          fd: null,
          mode: 0o666,
          autoClose: true
        };
    
        // response.pipe(fs.createWriteStream(dest, settings));
        var file = fs.createWriteStream(dest, settings);
        response.pipe(file);
    
        file.on('finish', function () {
          let okMsg = {
            text: `File downloaded successfully`
          }
          cb(okMsg);
          file.end(); 
        });
      }).on('error', function (err) { // Handle errors
        fs.unlink(dest); // Delete the file async. (But we don't check the result)
        let errorMsg = {
          text: `Error in file downloadin: ${err.message}`
        }
        if (cb) cb(errorMsg);
      });
    };
    
  • -4

    如果没有库,只需指出它就可能是错误的 . 以下是一些:

    我的建议在这里:

    • 调用系统工具,如 wgetcurl

    • 使用像node-wget-promise这样的工具也很简单 . var wget = require('node-wget-promise'); wget('http://nodejs.org/images/logo.svg');

  • 39

    您可以尝试使用 res.redirect 到https文件下载URL,然后它将下载该文件 .

    喜欢: res.redirect('https//static.file.com/file.txt');

  • -4
    var requestModule=require("request");
    
    requestModule(filePath).pipe(fs.createWriteStream('abc.zip'));
    
  • -1

    我们可以使用下载节点模块,它非常简单,请参考下面的https://www.npmjs.com/package/download

相关问题