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

NextJS 14结合Vercel Blob存储时Server Action异常求助

问题原因与修复方案

核心问题分析

你遇到的页面刷新、URL携带参数且Server Action未执行的问题,主要由以下三点导致:

  1. 客户端组件未声明'use client':EditImageModal使用了useRef客户端钩子,但未添加'use client'指令,Next.js会将其视为服务器组件,无法正确绑定Server Action,浏览器退化为默认GET表单提交。
  2. 表单未指定POST方法:未设置method="POST",浏览器默认用GET请求提交,导致URL携带表单参数并刷新页面(Server Action仅支持POST请求)。
  3. 缺少文件上传编码类型:上传文件的表单未设置enctype="multipart/form-data",无法正确传递文件数据到Server Action。

修复代码

1. 修改EditImageModal.tsx(客户端组件)

添加'use client'指令,同时修正表单属性:

'use client'; // 必须添加,标记为客户端组件

import {
  Button,
  Modal,
  ModalBody,
  ModalContent,
  ModalFooter,
  ModalHeader,
} from "@nextui-org/react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faArrowUpFromBracket } from "@fortawesome/free-solid-svg-icons";
import { useRef } from "react";

export default function EditImageModal(props: {
  isOpen: boolean;
  onClose: () => void;
  serverAction: (formData: FormData) => Promise<void>; // 修正为异步函数类型
}) {
  const fileInputRef = useRef<HTMLInputElement>(null);

  const handleClick = () => {
    if (fileInputRef.current) {
      fileInputRef.current.click();
    }
  };

  return (
    <Modal isOpen={props.isOpen} onOpenChange={props.onClose} placement="auto">
      {/* 添加POST方法与文件上传编码类型 */}
      <form action={props.serverAction} method="POST" enctype="multipart/form-data">
        <ModalContent>
          <ModalHeader className="flex flex-col gap-1">Edit Image</ModalHeader>
          <ModalBody>
            <div
              className={`w-full h-64 flex justify-center items-center flex-col gap-4 font-bold hover:cursor-pointer border-2 border-dashed rounded-lg border-black`}
              onClick={handleClick}
            >
              <FontAwesomeIcon
                icon={faArrowUpFromBracket}
                size="2x"
                color="#222222"
              />
              <span>Upload an image</span>
              <input
                ref={fileInputRef}
                type="file"
                accept="image/*"
                id="image"
                name="image"
                style={{ display: "none" }}
              />
            </div>
          </ModalBody>
          <ModalFooter className="flex justify-between">
            <Button color="default" variant="bordered" onPress={props.onClose}>
              Cancel
            </Button>
            <Button color="primary" type="submit">
              Save
            </Button>
          </ModalFooter>
        </ModalContent>
      </form>
    </Modal>
  );
}

2. 优化EditImageModalWrapper.tsx(服务器组件)

添加错误处理,提升稳定性:

import { put } from "@vercel/blob";
import { revalidatePath } from "next/cache";
import EditImageModal from "./EditImageModal";

export default async function EditImageModalWrapper(props: {
  isOpen: boolean;
  onClose: () => void;
}) {
  async function uploadImage(formData: FormData) {
    "use server";

    try {
      console.log("Server function called");

      const imageFile = formData.get("image") as File;
      if (!imageFile || imageFile.size === 0) {
        throw new Error("请选择要上传的图片");
      }

      const blob = await put(imageFile.name, imageFile, {
        access: "public",
      });

      console.log("上传成功:", blob);

      revalidatePath("/edit");
      revalidatePath("/");
    } catch (error) {
      console.error("上传失败:", error);
      throw new Error("图片上传失败,请重试");
    }
  }

  return (
    <EditImageModal
      isOpen={props.isOpen}
      onClose={props.onClose}
      serverAction={uploadImage}
    />
  );
}

修复说明

  • 'use client'指令:确保组件在客户端运行,正确处理useRef和Server Action的绑定逻辑。
  • method="POST":符合Server Action的请求要求,避免浏览器默认的GET提交行为。
  • enctype="multipart/form-data":是文件上传表单的必填属性,确保文件数据能正确传递到Server Action。
  • 错误处理:添加异常捕获,便于排查问题并给用户反馈。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 10:42:33