API Gateway+Lambda上传复杂PDF至S3出现空文件问题排查
问题:复杂PDF通过API Gateway+Lambda上传至S3后内容为空(页数保留)
架构:API Gateway + Lambda + S3存储桶,通过POST请求的form-data上传PDF文件。
- 简单PDF上传正常,内容完整;
- 复杂PDF上传后,S3中文件页数与原文件一致,但内容为空;
- 已确认API接收的原始内容与原文件完全一致。
接收请求的Lambda代码:
import { parse } from "parse-multipart-data" const contentType = event.headers["Content-Type"] || event.headers["content-type"] const boundary = contentType?.split("boundary=")[1] const parts = parse(Buffer.from(eventBody), boundary) let result = { file: {}, } as FileRequestDecoded for (let i = 0; i < parts.length; i++) { const part = parts[i] if (part.type === "application/pdf") { console.log("pdf found ", part.filename) result.file = { id: `${fileId}-${part.filename!}`.toLowerCase(), name: part.filename!, data: part.data, // 文件数据 } } }
上传至S3的代码:
await this.s3.putBufferData( this.bucketName, `pdfs/${fileData.file.id}`, fileData.file.data ) async putBufferData(bucketName: string, key: string, data: Buffer) { const command = new PutObjectCommand({ Bucket: bucketName, Key: key, Body: data, ContentType: 'application/pdf', }) await this.s3Client.send(command) }
排查方向与解决方案
1. 修正eventBody的编码处理
API Gateway在Lambda代理集成模式下,默认会将二进制payload编码为base64。如果你的eventBody是base64格式,直接用Buffer.from(eventBody)会导致二进制数据损坏——简单PDF字节量小可能未触发明显问题,但复杂PDF的二进制结构被破坏后,阅读器能识别页数元数据,但内容无法解析。
修正代码:
// 根据API Gateway标记判断是否需要base64解码 const bodyBuffer = event.isBase6Encoded ? Buffer.from(eventBody, 'base64') : Buffer.from(eventBody) const parts = parse(bodyBuffer, boundary)
2. 替换parse-multipart-data库
parse-multipart-data属于小众库,对复杂multipart/form-data结构(比如包含额外头部、特殊字符的PDF文件)的兼容性较差,容易出现解析截断或数据丢失。建议改用成熟的busboy库处理:
import Busboy from 'busboy' export const handler = async (event) => { return new Promise((resolve, reject) => { const busboy = new Busboy({ headers: event.headers, limits: { files: 1 } // 限制仅接收一个文件 }) let fileData: { id: string; name: string; data: Buffer } | undefined busboy.on('file', (fieldname, file, filename, encoding, mimetype) => { if (mimetype === 'application/pdf') { const chunks: Buffer[] = [] file.on('data', (chunk) => chunks.push(chunk)) file.on('end', () => { fileData = { id: `${fileId}-${filename}`.toLowerCase(), name: filename, data: Buffer.concat(chunks) } }) } else { file.resume() // 跳过非PDF文件 } }) busboy.on('finish', async () => { if (!fileData) return reject(new Error('未上传PDF文件')) await this.s3.putBufferData(this.bucketName, `pdfs/${fileData.id}`, fileData.data) resolve({ statusCode: 200, body: '上传成功' }) }) busboy.on('error', (err) => reject(err)) // 处理编码后的请求体 const body = event.isBase6Encoded ? Buffer.from(event.body, 'base64') : event.body busboy.write(body) busboy.end() }) }
3. 验证文件数据完整性
在解析完成后添加日志,对比原始请求与解析后数据的字节长度,确认是否存在数据丢失:
// 打印原始请求体字节数 console.log('原始请求体长度:', Buffer.from(eventBody).length) // 打印解析后PDF数据字节数 console.log('解析后PDF数据长度:', part.data.length)
如果长度不一致,说明解析过程中数据丢失,需更换解析库或检查边界提取逻辑。
4. 调整服务大小限制
- API Gateway默认最大请求payload为10MB,若复杂PDF超过此大小,需在API Gateway设置中调整
Maximum payload size; - Lambda内存建议设置为256MB以上,同时将超时时间调整至30秒左右,避免处理大文件时超时导致数据未完全上传。
内容的提问来源于stack exchange,提问作者Asif Alam
相关产品推荐
相关产品推荐

