【问题标题】:How do I recover from failure when uploading multiple files via my server to S3?通过我的服务器将多个文件上传到 S3 时,如何从失败中恢复?
【发布时间】:2020-02-18 05:04:23
【问题描述】:

NodeJS 和 S3 的新手,我编写了以下探索性代码,通过我的 NodeJS 服务器将文件上传到 S3,而无需将文件保存到磁盘或内存:

var express = require('express');
var Busboy = require('busboy');
var S3 = require('../utils/s3Util');
var router = express.Router(); // mounted at /uploads

router.post("/", function (req, res, next) {
    let bb = new Busboy({ headers: req.headers });
    const uploads = [];
    bb.on('file', (fieldname, stream, filename, encoding, mimeType) => {
        console.log(`Uploaded fieldname: ${fieldname}; filename: ${filename}, mimeType: ${mimeType}`);
        uploads.push(S3.svc.upload({ Bucket: 'my-test-bucket', Key: filename, Body: stream }).promise());
    });
    bb.on('finish', () => {
        console.log("# of promises:", uploads.length);
        Promise.all(uploads).then(retVals => {
            for (let i = 0; retVals && i < retVals.length; i++) {
                console.log(`File ${i + 1}::`, retVals[i]); 
            }
            res.end();
        }).catch(err => {
            console.log("Error::", err);
            res.status(500).send(`${err.name}: ${err.message}`);
        });
    });
    req.pipe(bb);
});

module.exports = router;

一般失败情况下,上传1个或多个x个文件上传失败的情况如何处理?有些上传会成功,有些会失败。但是,在catch 子句中,我不知道哪些失败了...

最好能够使这个上传过程有点事务性(即,要么所有上传都成功,要么不成功)。发生错误时,理想情况下我能够“回滚”成功上传的子集。

【问题讨论】:

    标签: javascript node.js amazon-s3 busboy


    【解决方案1】:

    你可以这样做:

    将一个对象推送到上传中,其中包含您需要重试的数据,所以:

    uploads.push({ 
      fieldname, 
      filename, 
      mimeType,
      uploaded: S3.svc.upload({ Bucket: 'my-test-bucket', Key: filename, Body: stream })
        .promise()
        .then(() => true)
        .catch(() => false)
    });
    
    ...
    
    const failed = await 
      (Promise.all(uploads.map(async upload => ({...upload, uploaded: await upload.uploaded})))).then(u => u.filter(upload => !upload.uploaded))
    
    const failedFiles = failed.join(', ')
    
    console.log(`The following files failed to upload: ${failedFiles}`);
    

    您需要让您的事件处理程序async 在其中使用await,例如:

    bb.on('file', async (fieldname, stream, filename, encoding, mimeType) => {
    

    【讨论】:

    • 一个非常有趣的解决方案,谢谢! file 事件的处理程序的签名不是由 busboy 模块决定的吗(我对 Javascript 还不够熟悉,对此不确定)?见:Busboy (special) events
    • 真实故事,没想到 - 这不是你的 API,所以你必须顺其自然!
    【解决方案2】:

    我最终使用了以下代码,这是@JoshWulf 答案的扩展:

    function handleUpload(req, res, bucket, key) {
        let bb = new Busboy({ headers: req.headers });
        const uploads = [];
        bb.on('file', (fieldname, stream, filename, encoding, mimeType) => {
            console.log(`Uploaded fieldname: ${fieldname}; filename: ${filename}, mimeType: ${mimeType}`);
            const params = { Bucket: bucket, Key: key, Body: stream, ContentType: mimeType };
            uploads.push({ filename, result: S3.svc.upload(params).promise().then(data => data).catch(err => err) });
        });
        bb.on('finish', async () => {
            const results = await Promise.all(uploads.map(async (upload) => ({ ...upload, result: await upload.result })));
            // handle success/failure with their respective objects
        });
        req.pipe(bb);
    }
    

    这里与@Josh Wulf's 答案的区别在于,在我的上传承诺中,我按原样返回返回的数据对象(如果成功)和返回的错误对象(如果失败)。这样我就可以在以后根据需要使用它们。

    【讨论】:

    • 不错的解决方案!干得好。这将对将来的某人有所帮助。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-07-30
    • 2013-06-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-07
    相关资源
    最近更新 更多