Javascript 使用Node.js下载大文件的时间复杂度应该更低
我想使用node.js开发一个高性能的文件下载程序。我可能需要下载高达10GB的文件。我已尝试使用内置节点模块。下面是代码:Javascript 使用Node.js下载大文件的时间复杂度应该更低,javascript,node.js,express,nginx,Javascript,Node.js,Express,Nginx,我想使用node.js开发一个高性能的文件下载程序。我可能需要下载高达10GB的文件。我已尝试使用内置节点模块。下面是代码: var fs = require('fs'); var http = require('http'); var file = fs.createWriteStream('download.bin'); var contentLength; var length; var responseData = ''; var timeDiff = 0; var fileurl
var fs = require('fs');
var http = require('http');
var file = fs.createWriteStream('download.bin');
var contentLength;
var length;
var responseData = '';
var timeDiff = 0;
var fileurl = 'http://speed.hetzner.de/1GB.bin';
var request = http.get(fileurl, function (response) {
timeDiff = new Date().getTime();
contentLength = parseInt(response.headers['content-length']); // in bytes
length = [];
// Grab the data buffer of the request
response.on('data', (d) => {
responseData += d;
length.push(d.length);
let sum = length.reduce((a, b) => a + b, 0);
let completedParcentage = (sum / contentLength) * 100;
console.log(`completed reading ${sum} bytes out of ${contentLength} bytes`);
console.log(`${completedParcentage} percentage of download complete`);
if (completedParcentage == 100) {
console.log(new Date().getTime() - timeDiff, 'check-this-now');
}
});
response.on('end', () => {
file.write(responseData);
console.log(new Date().getTime() - timeDiff, 'check-this-now');
});
});
我正在从API下载一个1GB文件。我花了115秒来完成。但我得到了一个错误:
FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - **JavaScript heap out of memory**
1: node::Abort() [node]
2: 0x557f33ccc011 [node]
3: v8::Utils::ReportOOMFailure(char const*, bool) [node]
4: v8::internal::V8::FatalProcessOutOfMemory(char const*, bool) [node]
5: v8::internal::Factory::NewRawOneByteString(int, v8::internal::PretenureFlag)
在阅读了文档之后,我知道我们需要指定一些标志来增加堆的大小
但是,有没有什么有效的方法可以在不使用任何内置模块标志的情况下实现这一点
如果没有办法,你能推荐任何模块或架构,比如使用NGINX或其他什么服务吗
注意:我还使用了“请求、请求进度”模块,它工作正常,但我也想知道其他解决方案。您的问题是:
responseData += d;
您正在将文件保存到RAM。无论您配置的节点堆有多大,这意味着您将需要至少10GB的RAM来缓冲文件(我承认我不知道您的硬件,您的机器可能有12GB或16GB的RAM。但我可用的机器的最大容量是8GB)。但最糟糕的是,如果您的需求发生了变化,需要下载20GB的文件,那么您需要升级硬件,使其具有20GB的RAM(或配置虚拟内存)。最重要的是,我甚至不确定节点是否可以配置10GB堆
而是使用硬盘缓冲下载的数据:
response.on('data', (d) => {
file.write(d); // THIS FIXES EVERYTHING
let sum += d.length;
let completedParcentage = (sum / contentLength) * 100;
console.log(`completed reading ${sum} bytes out of ${contentLength} bytes`);
console.log(`${completedParcentage} percentage of download complete`);
if (completedParcentage == 100) {
console.log(new Date().getTime() - timeDiff, 'check-this-now');
}
});
补充答复
如果您不想意外地部分下载文件(如果下载不完整),则可以按照浏览器的操作,在下载完成后重命名文件之前,先将其存储到临时文件中:
var file = fs.createWriteStream('download.temp');
// then later
response.on('end', () => {
file.end(()=>{
// finish closing file
fs.rename('download.temp', 'download.bin', () => {
// finish renaming file
console.log(new Date().getTime() - timeDiff, 'check-this-now');
});
});
});
我不是节点专家,但您不能将响应的输入/读取流通过管道传输到文件的输出/写入流吗?如果您等待HTTP响应的结束,然后立即读取正文并将其全部写入文件,那么您将整个文件加载到内存中,这就是您的问题所在。您必须使用缓冲区逐块编写它,或者使用流,这可能是最好的,因为它将由节点处理。一些好的读物:;)感谢您的回答。但是这种方法需要将近15分钟才能下载1 GB的文件。我正在寻找一个高性能的解决方案。有没有更好的方法来提高写入速度?@unnamedbull我怀疑磁盘写入是原因还是15分钟的下载。与原始方法相比,此方法几乎没有延迟。看看如何升级网络连接。15分钟内完成1GB文件意味着您有一个10Mbps的网络瓶颈