我需要从aws s3到节点js服务器下载大量文件(例如100k,每个文件大小为0.2-1 MB)。我正在使用的代码是
app.get('/api/download-all', function(req, res) {
res.json({status: 'download initiated'})
downloadFromS3(getDocs());
});
下载音频的功能是
function downloadFromS3(docs){
docs.forEach((doc, fileIndex)=>{
var s3FilePath = doc.wav
var fileName = s3FilePath.split('/').pop();
var s3Params = {Bucket: 'zzzzz', Key: s3FilePath};
var file = fs.createWriteStream(dir + '/' + fileName);
console.log(downloadSession);
s3.getObject(s3Params)
.on('httpData', function (chunk) {
console.log("file writing happening", fileName);
file.write(chunk);
})
.send();
}); }
在这里,下载功能会触发S3.getObject调用,与要下载的文件数量一样。它不等待文件状态。在允许下载文件之前,几乎已经制作了s3.getObject大约100k。这是正确的方法还是应该等待一个文件下载并在此之后调用s3调用。什么是正确的方法。
2)此代码还面临另一个问题。一旦从UI进行下载api调用,服务器就会开始忙于下载。它不会从用户界面返回任何请求。所有请求都待处理。无论如何,有在后台进行下载。我经历了一些方法,例如派生一个子进程或一个Web工作者来处理此问题。我不确定要使用哪个。最好的方法是什么?
答案 0 :(得分:0)
我建议采用一种中间方法。并行启动10万次下载确实不是一个好主意。但是类似地,等待每次下载完全完成不会占用您的全部带宽。我建议一种“合并”作业的解决方案-例如,您创建一个承诺池,每个承诺都可以一次下载一个文件,一旦完成,它就会开始下一个。
我一直在使用这样的函数:
Promise.pool = function pool(funcs, inParallel, progressCallback) {
const promises = [];
const results = [];
function getNext() {
if (funcs.length) {
return funcs.pop()()
.catch(() => {})
.then((res) => {
results.push(res);
if (progressCallback) {
progressCallback(results);
}
return getNext();
});
}
}
for (let i = 0; i < Math.min(inParallel, funcs.length); i++) {
promises.push(getNext());
}
return Promise.all(promises)
.then(() => results);
};
然后,您将定义一个函数数组,每个函数都下载一个文件并返回一个承诺,该承诺将在完成时解决:
const funcs = docs.map((doc) => {
return () => {
return new Promise((resolve) => {
var s3FilePath = doc.wav
var fileName = s3FilePath.split('/').pop();
var s3Params = {Bucket: 'zzzzz', Key: s3FilePath};
var file = fs.createWriteStream(dir + '/' + fileName);
console.log(downloadSession);
s3.getObject(s3Params)
.on('httpData', function (chunk) {
console.log("file writing happening", fileName);
file.write(chunk);
})
.on("end", () => resolve())
.send();
});
}
});
最后,您将像这样使用它:
const inParallel = 32;
function callback(partialResults) {
//console log, whatever
}
Promise.pool(funcs, inParallel, callback)
.then(() => console.log("all done!"));