将多个文件从AWS下载到Node JS服务器

时间:2019-06-27 00:50:06

标签: javascript node.js amazon-web-services amazon-s3 download

我需要从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工作者来处理此问题。我不确定要使用哪个。最好的方法是什么?

1 个答案:

答案 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!"));