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

Next.js14+Shadcn/Zod/RHF用户表单:图片上传等问题求助

问题解决方案

1. 图片上传编码错误

原因

Django接收文件上传要求表单必须使用multipart/form-data编码,且提交数据必须是FormData对象而非JSON格式。若直接用JSON提交文件,后端无法识别为合法文件类型。

解决步骤

  • 在表单上声明encType="multipart/form-data"
  • 提交时将表单数据转换为FormData对象传递,而非直接发送JSON

示例代码(ProfileForm.tsx):

import { useForm, setValue } from 'react-hook-form';
import { z } from 'zod';
import { zodResolver } from '@hookform/resolvers/zod';

const ProfileSchema = z.object({
  avatar: z.instanceof(File).optional(),
  user_id: z.number(),
  // 其他字段定义
});

type ProfileFormData = z.infer<typeof ProfileSchema>;

export function ProfileForm() {
  const { register, handleSubmit, setValue, formState: { errors } } = useForm<ProfileFormData>({
    resolver: zodResolver(ProfileSchema),
  });

  const onSubmit = async (data: ProfileFormData) => {
    const formData = new FormData();
    formData.append('user_id', data.user_id.toString());
    if (data.avatar) formData.append('avatar', data.avatar);
    // 其余字段依次添加到FormData中

    await createProfile(formData);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)} encType="multipart/form-data">
      <input
        type="file"
        ref={register('avatar').ref}
        onChange={(e) => {
          const file = e.target.files?.[0];
          file && setValue('avatar', file);
        }}
      />
      {/* 其他表单字段 */}
      <button type="submit">提交</button>
    </form>
  );
}

2. User ID类型不匹配问题

原因

表单提交时user_id被以字符串形式传递(比如输入框默认返回字符串),但Django后端期望接收数字类型主键,导致主键校验失败。

解决步骤

  • 在Zod Schema中自动完成字符串到数字的转换
  • 或在提交前手动转换类型

示例Zod Schema配置:

const ProfileSchema = z.object({
  user_id: z.string().transform(val => parseInt(val, 10)),
  // 其他字段
});

或提交时手动转换:

formData.append('user_id', parseInt(data.user_id as unknown as string, 10).toString());

3. {...field}处理图片输入崩溃问题

原因

React Hook Form的field对象包含的value属性为字符串/数字类型,而文件输入的value是FileList类型,直接展开{...field}会触发类型冲突,导致应用崩溃。

解决步骤

  • 不对文件输入直接使用{...field},手动拆分处理ref和onChange
  • 通过setValue手动更新表单中的文件值

示例代码:

<input
  type="file"
  ref={register('avatar').ref}
  onChange={(e) => {
    const file = e.target.files?.[0];
    if (file) setValue('avatar', file);
  }}
/>

补充:createProfile.ts请求配置

确保请求不手动设置Content-Type,浏览器会自动根据multipart/form-data生成正确的请求头:

export async function createProfile(formData: FormData) {
  const res = await fetch('/api/profile', {
    method: 'POST',
    body: formData,
    // 无需手动设置Content-Type
  });

  if (!res.ok) throw new Error('提交失败');
  return res.json();
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 02:12:46