我目前正在使用Shadcn、Zod和React Hook Form开发Next.js 14应用程序,并且在图像上传和表单验证方面遇到了一些问题。我有一个用于创建用户配置文件的表单,我使用createProfile函数提交表单数据,包括一个图像。然而,我遇到了以下问题:
-
图片上传问题-Django后端返回错误消息:“提交的数据不是文件。请检查表单上的编码类型。”。
-
用户Id问题-提交表单时,Django后端报告了一个与“用户”字段相关的错误:“无效的pk“0”-对象不存在。”我怀疑这与向Django后台发送字符串而不是数字有关。
-
注释字段输入问题-我在代码中注释了一种处理图像输入字段的替代方法。当我在该字段上使用{…field}方法时,我的整个应用程序在上传图像时会崩溃。我不知道为什么会发生这种情况,希望能深入了解这个问题。
这是我的代码(如果太大,很抱歉,我认为最好显示整个代码)
个人资料表格.tsx
"use client";
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import * as z from "zod";
import { useGlobalContext } from "@/components/context/GlobalContext";
import { toast } from "@/lib/use-toast";
import { createProfile } from "@/lib/users/createProfile";
import { Button } from "@/components/ui/button";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
// https://github.com/colinhacks/zod/issues/387
// https://github.com/blitz-js/blitz/discussions/2292#discussioncomment-826778
const profileFormSchema = z.object({
full_name: z.string().min(2).max(20),
address: z.string().min(2).max(50),
phone_number: z.string().min(2).max(20),
picture: z
.any()
.refine((file) => file?.length == 1, "File is required.")
.refine(
(file) =>
file[0]?.type === "image/png" ||
file[0]?.type === "image/jpeg" ||
file[0]?.type === "image/jpg",
"Must be a png, jpeg or jpg.",
)
.refine((file) => file[0]?.size <= 5000000, `Max file size is 5MB.`),
// picture: typeof window === "undefined" ? z.any() : z.instanceof(File),
});
export type ProfileFormSchemaType = z.infer<typeof profileFormSchema>;
export default function ProfileForm() {
const { userInfo, setUserInfo, accessToken } = useGlobalContext();
const form = useForm<ProfileFormSchemaType>({
resolver: zodResolver(profileFormSchema),
defaultValues: {
full_name: "",
address: "",
phone_number: "",
picture: undefined,
},
});
async function onSubmit(values: ProfileFormSchemaType) {
const formData = new FormData();
formData.append("picture", values.picture);
formData.append("full_name", values.full_name);
formData.append("address", values.address);
formData.append("phone_number", values.phone_number);
formData.append("user", String(userInfo.id));
console.log(formData);
try {
const response = await createProfile(formData, accessToken);
console.log(response);
toast({
title: "success",
});
} catch (error) {
toast({
title: "error",
description: error?.toString(),
});
console.error(error);
}
}
const fileRef = form.register("picture", { required: true });
return (
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="space-y-8 rounded-tr-2xl rounded-tl-2xl border p-8"
>
<FormField
control={form.control}
name="full_name"
render={({ field }) => (
<FormItem>
<FormLabel>Full name</FormLabel>
<FormControl>
<Input placeholder="John Doe" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="phone_number"
render={({ field }) => (
<FormItem>
<FormLabel>Phone number</FormLabel>
<FormControl>
<Input placeholder="+38269123123" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="address"
render={({ field }) => (
<FormItem>
<FormLabel>Address</FormLabel>
<FormControl>
<Input placeholder="Podgorica, Donja Gorica" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
{/* <FormField
control={form.control}
name="picture"
render={({ field }) => (
<FormItem>
<FormLabel>Picture</FormLabel>
<FormControl>
<Input
accept="image/png, image/jpeg, image/jpg"
type="file"
placeholder="profile image"
{...fileRef}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/> */}
<Label htmlFor="picture">Picture</Label>
<Input
id="picture"
type="file"
placeholder="profile image"
{...form.register("picture", { required: true })}
/>
{form.formState.errors.picture && (
<div>
<span>This field is required</span>
</div>
)}
<Button
disabled={form.formState.isSubmitting}
type="submit"
className={"block mx-auto px-8"}
>
Submit
</Button>
</form>
</Form>
);
}
以及在onSubmit中调用的createProfile函数:
createProfile.ts:
"use server";
import { env } from "@/lib/zod_schemas/envSchema";
type ErrorResponse = {
errors: ProfileErrorApiResponse;
};
export async function createProfile(
formData: FormData,
token: string,
): Promise<ProfileSuccessApiResponse | ErrorResponse> {
let response: Response;
try {
response = await fetch(`${env.API_BASE_URL}/products/profile/`, {
method: "POST",
body: formData,
headers: {
// "Content-Type": "multipart/form-data",
Authorization: `JWT ${token}`,
},
cache: "no-cache", // This option disables caching
});
} catch (error) {
console.error("Error: ", error);
throw new Error("Network error");
}
if (response.ok) {
const responseData: ProfileSuccessApiResponse = await response.json();
return responseData;
} else if (response.status === 400) {
const responseError: ProfileErrorApiResponse = await response.json();
return { errors: responseError };
} else {
throw new Error("Unexpected error. Please try again later!");
}
}
当我提交时,我从django后端收到了以下错误:
{
"errors":{
"picture":[
"The submitted data was not a file. Check the encoding type on the form."
],
"user":[
"Invalid pk \"0\" - object does not exist."
]
}
}
谢谢你的帮助。