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

Lambda函数文件上传正常,API Gateway下无法正常上传

解决FastAPI+Lambda通过API Gateway代理上传图片失败的问题

问题场景

开发了一个用于网站图片上传的FastAPI端点,部署到Lambda函数后直接访问端点时文件上传功能正常,但通过API Gateway代理访问时,图片无法正确上传。对比两者的请求日志,发现API Gateway未携带content-length请求头。

原代码如下:

from uuid import uuid4

import boto3
from fastapi import APIRouter, File, HTTPException, Request, UploadFile
from loguru import logger

from app.core.config import ImageRouterConfig


def upload_image_to_s3(file: UploadFile, bucket_name: str, cloudfront: str) -> str:
    s3 = boto3.resource(service_name="s3")
    filename = f"{uuid4()}{file.filename}"
    s3_key = f"images/{filename}"
    try:
        file.file.seek(0)
        s3.Bucket(bucket_name).upload_fileobj(
            Fileobj=file.file,
            Key=s3_key,
            ExtraArgs={"ContentType": file.content_type, "ACL": "public-read", "ContentEncoding": "base64"},
        )
    except Exception as e:
        raise HTTPException(status_code=500, detail="Could not upload file.") from e
    return f"{cloudfront}/{s3_key}"


def create_images_router(config: ImageRouterConfig) -> APIRouter:
    router = APIRouter()

    @router.post("")
    def post_upload_file(request: Request, file: UploadFile = File(...)) -> str:
        logger.info(f"Uploading file {file.filename}")
        logger.info(f"Request headers: {request.headers}")
        logger.info(f"Content type: {file.content_type}")
        s3_url = upload_image_to_s3(file, config.bucket_name, config.cloudfront_url)
        return s3_url

    return router

问题原因

  1. API Gateway代理multipart/form-data请求时,默认不会传递content-length请求头,FastAPI的UploadFile依赖该头来完整读取文件内容,导致通过网关上传时文件读取不完整。
  2. 原代码中设置了ContentEncoding: base64,但上传的图片本身并非Base64编码格式,会导致S3存储的文件损坏,无法正常显示。

解决方案

1. 配置API Gateway启用二进制媒体类型

  • 登录AWS控制台,进入API Gateway管理页面,找到目标API。
  • 切换到设置标签页,在二进制媒体类型中添加multipart/form-data。
  • 重新部署API到对应的阶段,确保配置生效。

2. 调整API Gateway集成请求设置

  • 在API Gateway的集成请求配置中,将内容处理设置为通过(Passthrough),避免API Gateway对请求体进行Base64编码或修改。
  • 若使用Lambda代理集成,确认请求体完整传递,无额外处理逻辑。

3. 修改FastAPI代码适配流式读取

由于API Gateway可能以流式方式传递请求体(无content-length),需要先将文件内容完整读取到内存再上传到S3,同时移除错误的编码设置:

修改后的代码:

from uuid import uuid4
import io
import boto3
from fastapi import APIRouter, File, HTTPException, Request, UploadFile
from loguru import logger
from app.core.config import ImageRouterConfig


def upload_image_to_s3(file: UploadFile, bucket_name: str, cloudfront: str) -> str:
    s3 = boto3.resource(service_name="s3")
    filename = f"{uuid4()}{file.filename}"
    s3_key = f"images/{filename}"
    try:
        # 将文件内容读取到内存,适配无content-length的流式场景
        file_content = io.BytesIO(file.file.read())
        file_content.seek(0)
        s3.Bucket(bucket_name).upload_fileobj(
            Fileobj=file_content,
            Key=s3_key,
            ExtraArgs={"ContentType": file.content_type, "ACL": "public-read"},
            # 移除ContentEncoding: base64,避免非编码文件损坏
        )
    except Exception as e:
        raise HTTPException(status_code=500, detail="Could not upload file.") from e
    return f"{cloudfront}/{s3_key}"


def create_images_router(config: ImageRouterConfig) -> APIRouter:
    router = APIRouter()

    @router.post("")
    def post_upload_file(request: Request, file: UploadFile = File(...)) -> str:
        logger.info(f"Uploading file {file.filename}")
        logger.info(f"Request headers: {request.headers}")
        logger.info(f"Content type: {file.content_type}")
        s3_url = upload_image_to_s3(file, config.bucket_name, config.cloudfront_url)
        return s3_url

    return router

4. 验证配置

  • 在Lambda日志中打印完整的请求事件,确认请求体是否正确传递。
  • 检查API Gateway阶段设置,确保未启用请求体压缩等可能修改请求的配置。

总结

通过启用API Gateway的二进制支持、调整集成请求设置,同时修改代码适配流式文件读取并移除错误的编码配置,即可解决通过API Gateway代理上传图片失败的问题。

内容的提问来源于stack exchange,提问作者Tom McLean

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 04:52:13