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

FastAPI用UploadFile上传到S3报expected string or bytes-like object错误如何解决

问题根因分析
  • 不要访问私有属性_file:SpooledTemporaryFile本身已经实现了类文件对象接口,完全可以直接传给upload_fileobj使用,不需要访问内部私有属性。私有属性属于Python内部实现,结构会随版本变化,访问反而会触发类型错误。
  • 同步方法误用await:如果使用的是标准同步boto3客户端,upload_fileobj是同步方法,不支持await调用,强制加await会直接触发类型异常,这是抛出「expected string or bytes-like object」的核心原因之一。
  • 文件指针位置异常:拿到UploadFile的file对象时,文件指针可能不在起始位置,读取时会拿到空内容,也可能触发异常。
  • 代码笔误:生成返回URL时使用了不存在的变量file.filename,你的参数名是fileobject,变量不存在也会触发异常。
  • 边界逻辑缺失:判断文件名后缀时没有处理文件无后缀的场景,rsplit取索引会直接抛出异常。
修复后的代码实现

以下是可直接运行的实现,不需要将文件保存到本地:

from fastapi import FastAPI, File, UploadFile, Body, HTTPException
import boto3
import logging
from typing import Optional

# 初始化S3客户端(同步客户端示例,适配所有S3兼容存储)
s3_client = boto3.client(
    's3',
    aws_access_key_id="你的访问密钥AK",
    aws_secret_access_key="你的访问密钥SK",
    endpoint_url="如果使用第三方S3兼容存储(如阿里云OSS、DO Spaces等)请填写对应endpoint,AWS S3可省略"
)

# 按自身业务需求调整配置
ALLOWED_EXTENSIONS = {"png", "jpg", "jpeg", "pdf", "docx"}
FILE_CONTENT_TYPES = {
    "png": "image/png",
    "jpg": "image/jpeg",
    "jpeg": "image/jpeg",
    "pdf": "application/pdf",
    "docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
}
BUCKET = "你的存储桶名称"
OBJ_URL = "你的存储桶公网访问前缀"

app = FastAPI()

# 此处用同步def避免同步S3调用阻塞事件循环,要保留async路由请改用aioboto3异步客户端
@app.post("/uploadFile")
def upload_file(
    fileobject: UploadFile = File(...),
    filename: Optional[str] = Body(default=None),
    key: str = Body(default=None),
):
    # 处理无后缀文件的边界场景
    if "." not in fileobject.filename:
        raise HTTPException(status_code=400, detail="File with no extension is not allowed")
    extension = fileobject.filename.rsplit(".", 1)[1].lower()

    if key is None:
        raise HTTPException(status_code=400, detail="Key is missing")
    if filename is None:
        filename = fileobject.filename
    
    if extension not in ALLOWED_EXTENSIONS:
        raise HTTPException(
            status_code=400, detail=f"File of type {extension} is not allowed"
        )
    
    try:
        # 将文件指针移到起始位置,避免读取空内容
        fileobject.file.seek(0)
        # 直接传入fileobject.file即可,不需要转换类型,同步方法不需要加await
        s3_client.upload_fileobj(
            fileobject.file,
            BUCKET,
            f"{key}/{filename}",
            ExtraArgs={
                "ACL": "public-read", # 标准公开读ACL,不要写public,非标准ACL不被大部分存储支持
                "ContentType": FILE_CONTENT_TYPES.get(extension, "application/octet-stream")
            }
        )
        object_url = f"{OBJ_URL}/{key}/{filename}"
        return {"file_url": object_url}
    except Exception as e:
        logging.error(f"Exception while uploading file - {e}")
        raise HTTPException(
            status_code=400, detail=f"Exception {e} while uploading the file"
        )
注意事项
  • 如果需要保留async异步路由,请使用aioboto3异步客户端,不要给同步boto3方法加await,会直接抛出类型错误。
  • 不要访问Python对象的下划线开头私有属性,这类属性没有兼容性保障,不同版本结构差异很大,极易触发异常。
  • S3的标准公开访问ACL是public-read,非标准的public值不被大部分S3兼容存储支持,会抛出权限错误。
  • 大文件上传可以配合分块上传逻辑优化性能,小文件直接用上述实现即可。

内容的提问来源于stack exchange,提问作者Ajitabh Pandey

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.07 09:18:04