【问题标题】:Download large file with node.js avoiding high memory consumption使用 node.js 下载大文件,避免高内存消耗
【发布时间】:2011-06-13 21:13:59
【问题描述】:

我正在尝试创建一个文件下载器作为后台服务,但是当安排一个大文件时,它首先被放入内存,然后在下载结束时将文件写入磁盘。

考虑到我可能同时下载大量文件,如何将文件逐渐写入磁盘保留内存?

这是我正在使用的代码:

var sys = require("sys"),
    http = require("http"),
    url = require("url"),
    path = require("path"),
    fs = require("fs"),
    events = require("events");

var downloadfile = "http://nodejs.org/dist/node-v0.2.6.tar.gz";

var host = url.parse(downloadfile).hostname
var filename = url.parse(downloadfile).pathname.split("/").pop()

var theurl = http.createClient(80, host);
var requestUrl = downloadfile;
sys.puts("Downloading file: " + filename);
sys.puts("Before download request");
var request = theurl.request('GET', requestUrl, {"host": host});
request.end();

var dlprogress = 0;


setInterval(function () {
   sys.puts("Download progress: " + dlprogress + " bytes");
}, 1000);


request.addListener('response', function (response) {
    response.setEncoding('binary')
    sys.puts("File size: " + response.headers['content-length'] + " bytes.")
    var body = '';
    response.addListener('data', function (chunk) {
        dlprogress += chunk.length;
        body += chunk;
    });
    response.addListener("end", function() {
        fs.writeFileSync(filename, body, 'binary');
        sys.puts("After download finished");
    });

});

【问题讨论】:

  • 您有机会分享最终结果吗?我正在寻找这样的东西......
  • 我尝试实现一个功能来跟踪 302 重定向,但我认为它不能正常工作。也许你可以试试。它是:gist.github.com/1297063

标签: javascript node.js


【解决方案1】:

您应该以追加模式写入文件,而不是将内容保存在 "data" 事件侦听器中的内存中。

【讨论】:

    【解决方案2】:

    我将回调改为:

    request.addListener('response', function (response) {
            var downloadfile = fs.createWriteStream(filename, {'flags': 'a'});
            sys.puts("File size " + filename + ": " + response.headers['content-length'] + " bytes.");
            response.addListener('data', function (chunk) {
                dlprogress += chunk.length;
                downloadfile.write(chunk, encoding='binary');
            });
            response.addListener("end", function() {
                downloadfile.end();
                sys.puts("Finished downloading " + filename);
            });
    
        });
    

    效果很好。

    【讨论】:

    • 难道我们不应该更喜欢 setEncoding(null) 而不是 'binary' 吗?
    • {'flags': 'a'} 会将数据附加到文件中(如果文件已存在)
    【解决方案3】:

    下载大文件时请使用fs.write而不是writeFile,因为它会覆盖之前的内容。

    function downloadfile(res) {
        var requestserver = http.request(options, function(r) {
            console.log('STATUS: ' + r.statusCode);
            console.log('HEADERS: ' + JSON.stringify(r.headers));
    
            var fd = fs.openSync('sai.tar.gz', 'w');
    
            r.on('data', function (chunk) {
                size += chunk.length;
                console.log(size+'bytes received');
                sendstatus(res,size);
                fs.write(fd, chunk, 0, chunk.length, null, function(er, written) {
                });
            });
            r.on('end',function(){
                console.log('\nended from server');
                fs.closeSync(fd);
                sendendstatus(res);
            });
        });
    }
    

    【讨论】:

    • fs.write 如果你不等待回调是不安全的。您应该使用 WriteStream。
    • 最好将res 传递到可写文件流。
    【解决方案4】:

    请求包是否适合您的使用?

    它可以让你做这样的事情:

    request(downloadurl).pipe(fs.createWriteStream(downloadtohere))
    

    【讨论】:

    • 你甚至不需要这个请求。只需将 reshttp.get 或任何正在使用的管道连接起来。
    • 它恰好在 4gb 时崩溃,知道为什么以及如何让它下载高达 10gb 的大文件吗?
    【解决方案5】:

    使用 Carter Cole 建议的流。这是一个更完整的例子

    var inspect = require('eyespect').inspector();
    var request = require('request');
    var filed = require('filed');
    var temp = require('temp');
    var downloadURL = 'http://upload.wikimedia.org/wikipedia/commons/e/ec/Hazard_Creek_Kayaker.JPG';
    var downloadPath = temp.path({prefix: 'singlePageRaw', suffix: '.jpg'});
    
    var downloadFile = filed(downloadPath);
    var r = request(downloadURL).pipe(downloadFile);
    
    
    r.on('data', function(data) {
      inspect('binary data received');
    });
    downloadFile.on('end', function () {
      inspect(downloadPath, 'file downloaded to path');
    });
    
    downloadFile.on('error', function (err) {
      inspect(err, 'error downloading file');
    });
    

    您可能需要安装可以通过以下方式执行的模块 npm install filed request eyespect temp

    【讨论】:

    • 没有理由使用 eyespect、filed 或 temp。这个例子很好,但看起来很臃肿。
    【解决方案6】:

    看看http-request

    // shorthand syntax, buffered response
    http.get('http://localhost/get', function (err, res) {
        if (err) throw err;
        console.log(res.code, res.headers, res.buffer.toString());
    });
    
    // save the response to 'myfile.bin' with a progress callback
    http.get({
        url: 'http://localhost/get',
        progress: function (current, total) {
            console.log('downloaded %d bytes from %d', current, total);
        }
    }, 'myfile.bin', function (err, res) {
        if (err) throw err;
        console.log(res.code, res.headers, res.file);
    });
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-02-26
      • 2011-10-28
      • 2014-08-06
      • 2010-10-12
      • 2017-10-03
      • 1970-01-01
      • 2010-10-19
      相关资源
      最近更新 更多