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

FastAPI双文件上传异常:添加Excel映射文件后报422错误如何解决?

解决FastAPI双文件上传的422错误(TIFF图片+Excel映射文件)

问题概述

构建支持TIFF图片与Excel映射文件上传的Web应用,原有单图片上传功能正常,添加映射文件读取逻辑后,前端提交请求返回422 (Unprocessable Entity)错误,提示image_file和mapping_file字段缺失。

错误原因

后端POST接口定义的接收参数名为image_file和mapping_file,但前端表单仅提供一个name="file"的文件输入框,字段名不匹配且缺少映射文件的输入项,导致FastAPI无法识别并接收所需参数。

修改方案

1. 前端HTML修改(dynamic.html)

添加两个文件输入框,分别对应后端的参数名,并设置文件类型限制与必填校验:

<html>
  <head>
    <title>TIFF图片与映射文件上传</title>
  </head>
  <body>
    <form action="/dynamic" enctype="multipart/form-data" method="POST">
      <div>
        <label>选择TIFF图片:</label>
        <input name="image_file" type="file" accept="image/tiff,image/*" required />
      </div>
      <div>
        <label>选择Excel映射文件:</label>
        <input name="mapping_file" type="file" accept=".xlsx,.xls" required />
      </div>
      <button type="submit">上传并处理</button>
    </form>

    {% if img %}
    <h2>处理后的图片</h2>
    <img src="data:image/png;base64,{{ img }}" />
    {% endif %}

    {% if mapping_table %}
    <h2>映射文件内容</h2>
    {{ mapping_table|safe }}
    {% endif %}
  </body>
</html>
  • 核心修改:输入框name属性必须与后端参数名完全一致,required确保双文件都上传,accept限制可选文件类型提升用户体验。

2. 后端Python修改(app.py)

优化文件读取逻辑、添加异常处理,并将Pandas DataFrame转为HTML表格供前端渲染:

from fastapi import FastAPI, UploadFile, File, Request, HTTPException
from fastapi.templating import Jinja2Templates
import base64
import io
import pandas as pd
from PIL import Image, ImageOps

app = FastAPI()

templates = Jinja2Templates(directory="templates")

@app.get("/")
def dynamic_file(request: Request):
    return templates.TemplateResponse("dynamic.html", {"request": request})

@app.post("/dynamic")
def dynamic(request: Request, image_file: UploadFile = File(...), mapping_file: UploadFile = File(...)):
    # 处理图片文件
    try:
        with image_file.file as f:
            image_contents = f.read()
        
        if image_file.content_type == "image/tiff":
            image = Image.open(io.BytesIO(image_contents))
            image_bytes = io.BytesIO()
            image.save(image_bytes, format="PNG")
            encoded_image = base64.b64encode(image_bytes.getvalue()).decode("utf-8")
        else:
            image = Image.open(io.BytesIO(image_contents))
            width, height = image.size
            new_size = (width // 2, height // 2)
            resized_image = ImageOps.fit(image, new_size)
            image_bytes = io.BytesIO()
            resized_image.save(image_bytes, format="PNG")
            encoded_image = base64.b64encode(image_bytes.getvalue()).decode("utf-8")
    except Exception as e:
        raise HTTPException(status_code=400, detail=f"图片处理失败: {str(e)}")

    # 处理映射文件
    try:
        with mapping_file.file as f:
            mapping_contents = f.read()
        
        mapping_df = pd.read_excel(io.BytesIO(mapping_contents))
        # 将DataFrame转换为HTML表格
        mapping_table = mapping_df.to_html(index=False)
    except Exception as e:
        raise HTTPException(status_code=400, detail=f"映射文件处理失败: {str(e)}")

    return templates.TemplateResponse(
        "dynamic.html", 
        {
            "request": request, 
            "img": encoded_image, 
            "mapping_table": mapping_table
        }
    )
  • 核心修改:用with语句自动关闭文件流,添加异常捕获返回友好错误,将DataFrame转为HTML字符串确保前端能正常渲染表格。

额外优化建议

  • 添加文件大小限制:在File()参数中设置max_length,例如File(..., max_length=10*1024*1024)限制单文件不超过10MB
  • 增强后端文件校验:通过文件名后缀或文件头检测,避免绕过前端限制的非法文件
  • 前端错误提示:利用FastAPI的错误响应,在页面展示上传失败的具体原因

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 17:15:09