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

Svelte上传图片至FastAPI后端持续返回422错误求助

图片上传时持续返回422 Unprocessable Entity错误排查

我尝试将图片上传至后端服务器时,持续收到INFO: 127.0.0.1:52154 - "POST /api/v1/contents/ HTTP/1.1" 422 Unprocessable Entity错误。最初使用Django REST Framework时就出现错误,以为是框架问题,换成FastAPI后仍遇到类似问题,推测可能与Svelte相关,但无法定位原因。我已添加如下请求头,错误依旧:

headers: {
    'Content-Type': 'multipart/form-data',
},

前端Svelte代码

<script lang="ts">
  import { fly } from 'svelte/transition';
  import type { SubmitFunction } from '@sveltejs/kit';
  import { addAlert } from '$lib/store/store';
  import { enhance } from '$app/forms';
  import type { Post } from '$lib/types';

  export let showPostModal: boolean;
  export let categories: any[];
  export let post: Post | null = null;
  export let actions: string;
  let errors: any = {};
  let loading: boolean = false;
  const dispatch = createEventDispatcher();
  const closeModal = () => {
    dispatch('close');
  };

  const submit: SubmitFunction = ({ formData }) => {
    loading = true;

    return async ({ result, update }) => {
      try {
        if (result.status === 200) {
          console.log("200 status...........", result);

          dispatch('postCreated', result.data.newPost);
          addAlert({
            message: `${post ? 'Post edited successfully' : 'New post created successfully'}`,
            type: 'success'
          });
          closeModal();
        } else if (result.status === 400) {
          console.log("400 error...........", result);
          errors = result?.data.errors;
        } else if (result.status === 500) {
          console.log("500 error..............", result);
          addAlert({ message: `${result.data.message}`, type: 'error' });
        }
      } finally {
        loading = false;
        update();
      }
    };
  };

  onMount(() => {
    categories = categories.map((category: any) => ({
      label: category.name,
      value: category.uuid
    }));
  });

  let photo: any[];
  let category: string = post ? post.category_name : '';
  let description: string = post ? post.description : '';
  let title: string = post ? post.title : '';
  $: author_name = post ? post.author_name : $currentUser?.name;

  console.log("author name...", currentUser.name, author_name);
</script>

{#if showPostModal}
  <div
    class="fixed inset-0 flex items-center justify-center z-50 left-0 transition-transform duration-300"
    in:fly={{ y: -100 }}
    out:fly={{ y: -100 }}
    class:slide-transition={{ showPostModal }}
  >
    <div
      on:click={closeModal}
      on:keydown={closeModal}
      class="w-full cursor-pointer h-full absolute bg-black opacity-30"
    />
    <div
      on:keydown={closeModal}
      class="absolute w-8 z-[100] h-8 items-center flex justify-center cursor-pointer bg-white rounded-full font-bold top-3 right-3 text-3xl text-rose-600"
      on:click={closeModal}
    >
      <iconify-icon icon="material-symbols:cancel" />
    </div>
    <div
      class="relative transform overflow-hidden rounded-lg bg-white text-left shadow-xl transition-all sm:my-8 mx-auto sm:max-w-xl md:max-w-[80rem]"
    >
      <form
        method="post"
        use:enhance={submit}
        enctype="multipart/form-data"
        action={actions}
        class="p-4 text-center flex h-[500px] flex-col gap-5 overflow-y-auto"
      >
        <h1 class="mb-1 text-xl font-bold">{post ? 'Edit Post' : 'Create Post'}</h1>

        <input type="file" name="photo" accept="image/*"/>

        <div class="mt-5 flex justify-center w-full">
          <button
            type="submit"
            class="py-2.5 outline-none px-4 inline-flex justify-center items-center gap-2 rounded-md border border-transparent font-semibold bg-primary text-white hover:bg-blue-900 w-full focus:ring-offset-2 transition-all text-sm"
          >
            {#if loading}
              <iconify-icon icon="line-md:loading-loop" width="20" />{:else}
              {post ? 'Edit Post' : 'Create Post'}
            {/if}
          </button>
        </div>
      </form>
    </div>
  </div>
{/if}
</script>

后端FastAPI代码

from fastapi import File, UploadFile, HTTPException, status

@router.post("/contents/")
async def create_content(photo: UploadFile = File(..., max_length=10*1024*1024)):
    print(photo, photo.filename, photo.content_type)

错误原因分析及解决方案

核心原因

手动设置Content-Type: multipart/form-data是关键问题。浏览器在处理文件上传时,会自动生成包含boundary(分隔符)的完整Content-Type头(例如multipart/form-data; boundary=----WebKitFormBoundaryxxxx),手动固定头会丢失boundary,导致后端无法正确解析表单数据,直接触发422错误。

具体解决步骤

  1. 移除手动设置的Content-Type头
    删除你添加的手动设置请求头的代码,让浏览器自动生成符合规范的请求头。

  2. 验证表单数据是否正确携带文件
    在submit函数中添加日志,确认文件被正确加入FormData:

    const submit: SubmitFunction = ({ formData }) => {
      console.log('表单中的文件:', formData.get('photo')); // 检查是否有File对象输出
      loading = true;
      // 原有逻辑不变
    };
    

    如果没有输出文件对象,检查input元素是否正常渲染,或是否有其他JS代码干扰表单数据。

  3. 后端增加错误日志定位细节
    修改后端代码,返回具体错误信息,方便排查:

    from fastapi import File, UploadFile, status
    from fastapi.responses import JSONResponse
    
    @router.post("/contents/")
    async def create_content(photo: UploadFile = File(..., max_length=10*1024*1024)):
        try:
            print(photo, photo.filename, photo.content_type)
            return {"message": "上传成功"}
        except Exception as e:
            return JSONResponse(
                status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
                content={"detail": str(e)}
            )
    
  4. 用浏览器工具检查请求
    打开开发者工具的Network面板,查看POST请求的Form Data部分,确认photo字段存在且包含文件数据。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 16:43:21