You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Node.js中如何将req.files.resume.data转为createReadStream(Lambda上传S3)

解决AWS Lambda+API Gateway环境下Buffer转可读流上传S3的问题

看起来你踩了两个关键的坑:一是对fs.createReadStream()的参数理解有误,二是没充分利用Lambda环境下的Buffer直接上传能力,我来帮你理清思路并解决问题:

错误原因分析

fs.createReadStream()的设计初衷是读取本地文件系统的文件,它接受的参数是文件路径、文件描述符这类文件系统资源,并不直接支持Buffer作为输入。你写的Buffer.from(resume.data.toString())完全是多余操作——把Buffer转成字符串再转回Buffer,既浪费性能,又不符合createReadStream的参数要求,这就是你收到TypeError [ERR_INVALID_ARG_VALUE]的根本原因。

另外,在Lambda环境下,本地文件系统只有/tmp目录可写,且临时存储有大小限制,完全没必要把Buffer写入本地再读取,直接用Buffer或者转成内存流更高效。

解决方案

根据你的需求(上传大文件到S3),分两种情况给出方案:

方案1:直接用Buffer上传(适合文件≤100MB,API Gateway最大payload限制)

AWS SDK的putObject方法直接支持传入Buffer作为Body参数,不需要转成流,代码更简洁:

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

router.post('/update-resume', authUtil.ensureAuthenticated, function(req, res, next){ 
  if(!req.files){ 
    res.status(400).json({message: "No file"}); 
  } else if(req.files.resume){ 
    const resume = req.files.resume; 
    if(resume.mimetype === "video/mp4" || resume.mimetype === "video/quicktime"){ 
      // 构建S3上传参数
      const s3UploadParams = {
        Bucket: '你的S3存储桶名称',
        Key: `resumes/${Date.now()}-${resume.name}`, // 自定义文件路径和名称
        Body: resume.data, // 直接传入Buffer
        ContentType: resume.mimetype // 保留原文件的MIME类型
      };

      // 执行上传
      s3.putObject(s3UploadParams, (err, uploadResult) => {
        if(err) {
          console.error('S3上传失败:', err);
          return res.status(500).json({message: "文件上传失败", error: err.message});
        }
        res.status(200).json({message: "文件上传成功", data: uploadResult});
      });
    } else {
      res.status(400).json({message: "仅支持MP4/MOV格式视频"});
    }
  } 
});

方案2:将Buffer转成可读流(适合大文件分块上传)

如果你的文件超过100MB(需要用S3分块上传),可以用Node.js的PassThrough流把Buffer转成可读流,这样就能兼容S3的uploadcreateMultipartUpload方法:

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

router.post('/update-resume', authUtil.ensureAuthenticated, function(req, res, next){ 
  if(!req.files){ 
    res.status(400).json({message: "No file"}); 
  } else if(req.files.resume){ 
    const resume = req.files.resume; 
    if(resume.mimetype === "video/mp4" || resume.mimetype === "video/quicktime"){ 
      // 创建PassThrough流,将Buffer转成可读流
      const fileStream = new PassThrough();
      fileStream.end(resume.data); // 把Buffer写入流

      // 构建S3上传参数
      const s3UploadParams = {
        Bucket: '你的S3存储桶名称',
        Key: `resumes/${Date.now()}-${resume.name}`,
        Body: fileStream,
        ContentType: resume.mimetype
      };

      // 用s3.upload方法支持自动分块(适合大文件)
      s3.upload(s3UploadParams, { partSize: 10 * 1024 * 1024 }, (err, uploadResult) => {
        if(err) {
          console.error('S3分块上传失败:', err);
          return res.status(500).json({message: "文件上传失败", error: err.message});
        }
        res.status(200).json({message: "文件上传成功", data: uploadResult});
      });
    } else {
      res.status(400).json({message: "仅支持MP4/MOV格式视频"});
    }
  } 
});

额外注意事项

  • API Gateway payload限制:默认API Gateway只允许最大10MB的请求,如果你要上传更大的文件,需要在API Gateway控制台把payload限制调到最大100MB;如果文件超过100MB,建议用S3预签名URL让客户端直接上传到S3,避免通过API Gateway中转。
  • Lambda内存与超时:上传大文件时,要确保Lambda配置了足够的内存(影响CPU和网络性能)和超时时间,避免上传过程中被中断。

内容的提问来源于stack exchange,提问作者Nitin Muchhadiya

火山引擎 最新活动