You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 04:46:35