从S3 Bucket向AWS Lambda传输大文件时遇到的问题
AWS Lambda处理S3大文件下载问题
问题描述
- 基于AWS Lambda实现S3上传事件触发逻辑:从S3获取文件并存入
/tmp目录,小文件处理正常,但15MB以上文件失败 - 已尝试调整Lambda内存、临时存储(ephemeral storage)、超时时间,问题未解决
- 改用分块下载代码后仍失败,且CloudWatch无相关日志输出(对应15MB文件传输的日志截图无有效内容)
分块下载代码
const fs = require('fs'); const path = require('path'); const s3 = new AWS.S3(); exports.handler = async (event) => { const s3Params = { Bucket: event.Records[0].s3.bucket.name, Key: decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, ' ')) }; const tempFilePath = path.join('/tmp', s3Params.Key); const fileSize = (await s3.headObject(s3Params).promise()).ContentLength; const chunkSize = 1024 * 1024; // 1 MB chunks for (let i = 0; i < fileSize; i += chunkSize) { const rangeEnd = Math.min(i + chunkSize - 1, fileSize - 1); const range = `bytes=${i}-${rangeEnd}`; const chunkParams = { ...s3Params, Range: range, }; const { Body } = await s3.getObject(chunkParams).promise(); if (i === 0) { fs.writeFileSync(tempFilePath, Body); } else { fs.appendFileSync(tempFilePath, Body); } } console.log(`File downloaded to ${tempFilePath}`); console.log("File size is " + fs.statSync(tempFilePath).size ); fs.unlink(tempFilePath, (err) => { if (err) throw err; console.log('deleted successfully'); }); };
问题排查与解决方案
1. 日志无输出核心原因
Lambda执行可能在触发日志前已崩溃,优先检查:
- 权限缺失:确认Lambda角色拥有
s3:GetObject、s3:HeadObject权限,以及CloudWatch Logs的logs:CreateLogStream、logs:PutLogEvents权限 - 同步IO阻塞:代码中使用
fs.writeFileSync、fs.appendFileSync同步方法,大文件分块时会阻塞事件循环,导致Lambda超时或崩溃,无法输出日志
2. 代码优化方案
改用异步流下载(比分块更高效,避免阻塞):
const fs = require('fs').promises; const path = require('path'); const { createWriteStream } = require('fs'); const { pipeline } = require('stream/promises'); const s3 = new AWS.S3(); exports.handler = async (event) => { const s3Params = { Bucket: event.Records[0].s3.bucket.name, Key: decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, ' ')) }; // 避免Key含路径导致/tmp下创建无效目录,仅提取文件名 const tempFilePath = path.join('/tmp', path.basename(s3Params.Key)); // 通过流直接下载文件,减少内存占用与阻塞 const s3Stream = s3.getObject(s3Params).createReadStream(); const fileStream = createWriteStream(tempFilePath); try { await pipeline(s3Stream, fileStream); const stats = await fs.stat(tempFilePath); console.log(`文件已下载至${tempFilePath},大小:${stats.size}字节`); await fs.unlink(tempFilePath); console.log('临时文件已删除'); } catch (err) { console.error('处理过程出错:', err); // 出错时清理临时文件 try { await fs.access(tempFilePath); await fs.unlink(tempFilePath); } catch (unlinkErr) { console.error('删除临时文件失败:', unlinkErr); } throw err; // 抛出错误标记Lambda执行失败 } };
3. 额外配置检查
- 路径合法性:避免S3 Object Key包含特殊字符或多级路径,改用
path.basename提取纯文件名存入/tmp - 超时与存储:根据文件大小调整Lambda超时时间(建议设为300秒),确认临时存储配额足够(最大10GB,默认512MB)
- 内存配置:Lambda内存与网络带宽正相关,建议配置1GB以上内存提升大文件下载速度
内容的提问来源于stack exchange,提问作者Mark Allen Molina
相关产品推荐
相关产品推荐

