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的upload或createMultipartUpload方法:
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




