我希望我的 Next.js/React 应用程序允许用户将图像文件上传到存储。
我的猜测是,最好将应用程序的所有存储处理部分放在应用程序的后端部分(到服务器操作),因为一些数据(例如带有凭据处理的存储处理)最好远离前端和浏览器控制台。
当我尝试将文件对象传递给我的服务器操作时,出现错误:
错误:只有普通对象和一些内置对象可以传递给服务器操作。不支持类或空原型。
我的代码是两个文件page.tsx:
'use client'
import { uploadServerAction } from "./storageStuff";
import React, { ChangeEventHandler, MouseEventHandler, useState } from "react";
export default function Home() {
const [file, setFile] = useState<File | null>(null);
const handleFileChange: ChangeEventHandler<HTMLInputElement> = (e) => {
e.preventDefault();
setFile(e.target.files![0]);
};
const handleUpload: MouseEventHandler<HTMLButtonElement> = async (e) => {
e.preventDefault();
uploadServerAction(file)
}
return (
<>
<div>
<input type="file" onChange={handleFileChange} />
<button type="button" onClick={handleUpload}>
Upload Image
</button>
</div>
</>
);
}
和storageStuff.ts:
'use server'
export async function uploadServerAction(file: File | null) {
'use server'
...
}
所以现在我有几个问题:
我是 React/Next.js 的新手,已经阅读了一些官方文档和一些操作方法。所以我的代码可能有非常愚蠢的东西。
我遇到了类似的问题,我找到的解决方案是使用表单操作。
"use client"
export default function UploadFile({ path }: { path: string }) {
const [error, action] = useFormState(writeFile.bind(null, path), {});
const formRef = useRef(null);
return (
<form action={action} ref={formRef}>
<Label htmlFor="file" className="cursor-pointer">
<span className="sr-only">upload file</span>
<UploadIcon />
</Label>
<Input type="file" id="file" name="file" />
{error?.file && <div className="text-destructive">{error.file}</div>}
<SubmitButton />
</form>
);
}
function SubmitButton() {
const { pending } = useFormStatus();
return (
<Button type="submit" disabled={pending}>
{pending ? "Saving..." : "Save"}
</Button>
);
}
表单动作是
"use server"
import fs from "fs/promises";
import { File } from "buffer";
import { z } from "zod";
import { revalidatePath } from "next/cache";
const fileSchema = z.object({
file: z
.instanceof(File, { message: "Required" })
.refine((file) => file.size > 0, "Required"),
});
export async function writeFile(
path: string,
prevState: unknown,
formData: FormData
) {
// validating form data
const result = fileSchema.safeParse(Object.fromEntries(formData.entries()));
if (result.success === false) {
return result.error.formErrors.fieldErrors;
}
const file = result.data.file;
await fs.writeFile(
`${path}/${file.name}`,
Buffer.from(await file.arrayBuffer())
);
revalidatePath("/");
}