一次将大量文件上传到AWS S3

时间:2018-02-02 14:04:13

标签: node.js amazon-web-services express amazon-s3 file-upload

我有一个应用,需要上传超过100,000 files(每个1MB)到S3 Bucket。我知道S3有api用于上传一个大文件,但是想知道他们是否有api用于上传大量文件。

我目前正在使用putObject并尝试upload api将我的文件上传到S3。问题是上传时间太长(浏览器超时后完成)并使用大量内存(超过512MB允许限制)。

保存文件的代码:

saveFile: async (fileUrl, data) => {
    await s3.putObject({
        Bucket: bucket,
        Key: fileUrl,
        Body: JSON.stringify(data)
    }).promise();
},

在另一个地方,我把saveFile放在这样的循环中:

for (let file of files) {
    await saveFile(file.url, file.data);
}

我搜索了解决方案并发现stream可能有助于减少内存需求,但我想知道时间有什么不同吗?如果是的话,我该如何实现呢?感谢。

2 个答案:

答案 0 :(得分:3)

我通常更喜欢使用managed upload API而不是putObject方法。它处理具有多重上传内容的大型文件,并且它支持流(您不能使用带有putObject的流,因为该API需要总文件大小)。

例如,来自Node:

const fs = require('fs');
const AWS = require('aws-sdk');
const s3 = new AWS.S3({});

s3.upload({
  Bucket: 'xxx',
  Key: 'fileName.png',
  Body: fs.createReadStream('/home/bar/Desktop/fileName.png')
}).promise(); // or callback

这可能会解决您与内存相关的问题,但可能不会加快上传速度。 for循环的问题在于它会一个接一个地串行上传对象。相反,您可以使用await Promise.all([/* your list*/].map(/* ... */)),它将并行执行所有上传 - 但是 - 100,000到目前为止是一个太大的数字。

我建议使用像async这样的库,它有很多有用的方法来处理异步操作组。 例如,您可以使用cargoqueue方法,您的代码如下所示:

const PARALLEL_UPLOADS = 10;
const q = async.queue((task, callback) => {
  s3.upload({
    Bucket: 'xxx',
    Key: task.dest,
    Body: fs.createReadStream(task.src)
  }, callback)
}, PARALLEL_UPLOADS);

q.drain = function() {
    console.log('all items have been processed');
};

q.push([
    { src: 'image1.png', dest: 'images/image1.png' },
    { src: 'image2.png', dest: 'images/image2.png' },
]);

这将同时上传所有文件,最多10个项目。

希望这有帮助,

答案 1 :(得分:0)

const AWS = require('aws-sdk');
const fs = require('graceful-fs'); // from node.js
const path = require('path'); // from node.js
const queue = require('async-promise-queue');
const s3 = new AWS.S3();

const pushS3 = (srcFolderPath, destFolderPath) => {
  const uploadPromise = [];
  console.log(`Pushing ${srcFolderPath} to S3`);

  const files = fs.readdirSync(srcFolderPath);
    if (!files || files.length === 0) throw new Error(`provided folder '${srcFolderPath}' is empty or does not exist.`);

  // for each file in the directory
  for (const fileName of files) {
    // get the full path of the file
    const filePath = path.join(srcFolderPath, fileName);

   // ignore if directory
   if (fs.lstatSync(filePath).isDirectory()) {
     continue;
   }

   uploadPromise.push({
     src: filePath,
     dest: `${destFolderPath}${fileName}`,
   });
  }
  const worker = queue.async.asyncify(task => s3.upload({
    Bucket: AWS_BUCKET,
    Key: task.dest,
    Body: fs.createReadStream(task.src),
  }).promise());

  return queue(worker, uploadPromise, 10000);
};

pushS3('sourcePath', 'destinationS3Path')
.then(()=>{console.log('Sucessfully Transferred to S3');})
.catch((err)=>{console.error(err);})

保证上述Simone Lusenti解决方案的实现。就我而言,我有50000+个奇数文件。我试图将这些文件从AWS ECS放入S3。我之前有2个错误。通过EMFILE too many files open模块,通过graceful-fs模块和AWS ECS中的Missing Credentials Error解决了async-promise-queue错误。