我想使用node.js开发一个高性能的文件下载器。我可能需要下载最大 10GB 的文件。我尝试在内置 Node 模块中使用。下面是代码:
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 下载 1 GB 文件。我需要 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 等提供服务?
注意:我还使用了 request、request-progress 模块,它效果很好,但我也想知道其他解决方案。
最佳答案
您的问题是这样的:
responseData += d;
您正在将文件保存到 RAM。无论您配置 Node 堆有多大,这都意味着您至少需要 10GB RAM 来缓冲文件(我承认我不了解您的硬件,您可能拥有一台具有 12GB 或 16GB RAM 的机器。但是机器可用于我的最大容量为 8GB)。但最糟糕的是,如果您的需求发生变化并且需要下载 20GB 文件,您将需要升级硬件以拥有 20GB RAM(或配置虚拟内存)。除此之外,我什至不确定 Node 是否可以配置 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');
});
});
});
关于javascript - 使用 Node.js 下载大文件的时间复杂度应该较低,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59517331/