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

FastAPI返回PDF文件,OpenAPI生成的Python客户端报UnicodeDecodeError

问题:OpenAPI Generator Python客户端解码PDF二进制数据触发UnicodeDecodeError

我正在构建一个系统,其中FastAPI服务端接口以二进制数据形式返回PDF文件,使用OpenAPI Generator Python(7.6.0、7.9.0版本)生成客户端。但调用该接口时,客户端会尝试将二进制PDF数据解码为UTF-8文本,导致出现UnicodeDecodeError。

已尝试的解决方案(均无效)

  • 尝试传入_preload_content=False以阻止客户端自动解码响应,但生成的客户端方法不接受该参数,引发Pydantic验证错误;
  • 使用StreamingResponse返回响应,问题依旧;
  • 通过API文档UI或curl测试接口,可正常接收PDF文件。

服务端代码(FastAPI 接口)

from fastapi import APIRouter, HTTPException, BackgroundTasks
from fastapi.responses import FileResponse, StreamingResponse
import os

router = APIRouter()

@router.get(
    "/purchases/{order_id}/shipment_page",
    responses={
        200: {
            "description": "PDF file response",
            "content": {
                "application/pdf": {
                    "schema": {
                        "type": "string",
                        "format": "binary"
                    }
                }
            }
        }
    }
)
async def get_shipment_page_route(order_id: str, background_tasks: BackgroundTasks):
    temp_file_path = f"temp_purchase_{order_id}.pdf"
    try:
        # 获取PDF内容(二进制数据)
        pdf_content = get_shipment_page(order_id)  # 返回bytes类型
        
        # 将PDF内容写入临时文件
        with open(temp_file_path, "wb") as temp_file:
            temp_file.write(pdf_content)
        
        headers = {
            "Content-Disposition": f"attachment; filename=Purchase_{order_id}.pdf",
        }

        # 响应发送后删除临时文件
        background_tasks.add_task(remove_file, temp_file_path)

        # 返回PDF文件
        return FileResponse(
            path=temp_file_path,
            filename=f"Purchase_{order_id}.pdf",
            media_type="application/pdf",
            headers=headers
        )
    except Exception as e:
        if os.path.exists(temp_file_path):
            os.remove(temp_file_path)
        raise HTTPException(status_code=500, detail=str(e))

def remove_file(path: str):
    if os.path.exists(path):
        os.remove(path)

客户端代码(OpenAPI生成的客户端)

import retumilo_client
from retumilo_client.rest import ApiException
import frappe
import logging


configuration = retumilo_client.Configuration(...)

def get_purchase_order_shipment_page(order_id: str):
    with retumilo_client.ApiClient(configuration) as api_client:
        # 创建API实例
        api_instance = retumilo_client.DefaultApi(api_client)

        try:
            # 获取PDF内容
            pdf_content = api_instance.get_shipment_page_route_purchases_order_id_shipment_page_get(order_id)
            return pdf_content
        except ApiException as e:
            logger.error(f"调用DefaultApi时异常: {e}")
            raise ValueError("获取发货页面失败")
        except Exception as e:
            logger.error(f"意外错误: {e}")
            raise ValueError("发生意外错误")

错误栈(Traceback)

22:26:38 web.1      | Traceback (most recent call last):
22:26:38 web.1      |   File "apps/xxxx_integration/xxxx_integration/xxxx_integration/integration/purchase_order.py", line 81, in get_purchase_order_shipment_page
22:26:38 web.1      |     pdf_content = api_instance.get_shipment_page_route_purchases_order_id_shipment_page_get(order_id)
22:26:38 web.1      |   File "env/lib/python3.10/site-packages/pydantic/validate_call_decorator.py", line 59, in wrapper_function
22:26:38 web.1      |     return validate_call_wrapper(*args, **kwargs)
22:26:38 web.1      |   File "env/lib/python3.10/site-packages/pydantic/_internal/_validate_call.py", line 81, in __call__
22:26:38 web.1      |     res = self.__pydantic_validator__.validate_python(pydantic_core.ArgsKwargs(args, kwargs))
22:26:38 web.1      |   File "env/lib/python3.10/site-packages/retumilo_client/api/default_api.py", line 1157, in get_shipment_page_route_purchases_order_id_shipment_page_get
22:26:38 web.1      |     return self.api_client.response_deserialize(
22:26:38 web.1      |   File "env/lib/python3.10/site-packages/retumilo_client/api_client.py", line 318, in response_deserialize
22:26:38 web.1      |     response_text = response_data.data.decode(encoding)
22:26:38 web.1      | UnicodeDecodeError: 'utf-8' codec can't decode byte 0x9c in position 197: invalid start byte

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 06:25:11