可以吗?如何将图像文件传递给 React 中的服务器操作?

问题描述 投票:0回答:1

我希望我的 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 的新手,已经阅读了一些官方文档和一些操作方法。所以我的代码可能有非常愚蠢的东西。

reactjs image file-upload server-action
1个回答
0
投票

我遇到了类似的问题,我找到的解决方案是使用表单操作。

"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("/");
}
© www.soinside.com 2019 - 2024. All rights reserved.