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

Next.js 15中Server Action读取静态文件部署失败问题

部署后Server Action读取文件失败问题

复现步骤

  • 访问vibrant-ui部署后的站点,进入docs/components/month-slider页面
  • 依次点击:Components > Month Slider > Code

预期结果

组件应正常显示代码。

当前情况

页面显示错误提示:Failed to read file

问题详情

我编写了一个Server Action用于读取components/vibrant/component-name.tsx路径下的静态组件文件,代码如下:

Server Action 代码

"use server"

import { promises as fs } from "fs"
import path from "path"
import { z } from "zod"

// 定义响应类型
type CodeResponse = {
  content?: string
  error?: string
  details?: string
}

// 输入验证规则
const fileSchema = z.object({
  fileName: z.string().min(1),
})

export async function getFileContent(fileName: string): Promise<CodeResponse> {
  // 验证输入参数
  try {
    fileSchema.parse({ fileName })
  } catch {
    return {
      error: "File parameter is required",
    }
  }

  try {
    // 安全拼接文件路径
    const filePath = path.join(process.cwd(), "components", "vibrant", fileName)

    const content = await fs.readFile(filePath, "utf8")

    return { content }
  } catch (error) {
    console.error("读取文件出错:", error)
    const errorMessage =
      error instanceof Error ? error.message : "未知错误"

    return {
      error: "Failed to read file",
      details: errorMessage,
    }
  }
}

我在客户端组件中调用该Server Action,代码如下:

客户端调用组件代码

"use client"

import { getFileContent } from "@/app/actions/file"
import { Button } from "@/components/ui/button"
import { cn } from "@/lib/utils"
import { Check, Copy } from "lucide-react"
import { useEffect, useState } from "react"
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"
import { oneDark } from "react-syntax-highlighter/dist/esm/styles/prism"

type Props = {
  source: string
  language?: string
}

export const CodeBlock = ({ source, language = "typescript" }: Props) => {
  const [code, setCode] = useState("")
  const [error, setError] = useState("")
  const [copied, setCopied] = useState(false)
  const [isExpanded, setIsExpanded] = useState(false)

  useEffect(() => {
    const fetchCode = async () => {
      const result = await getFileContent(source)

      if (result.error) {
        setError(result.error)
        setCode("")
        return
      }

      if (result.content) {
        setCode(result.content)
        setError("")
      }
    }

    fetchCode()
  }, [source])

  const copyToClipboard = async () => {
    try {
      await navigator.clipboard.writeText(code)
      setCopied(true)
      setTimeout(() => setCopied(false), 2000)
    } catch (err) {
      console.error("复制文本失败: ", err)
    }
  }

  if (error) {
    return <div className="p-4 bg-red-50 text-red-600 rounded-lg">{error}</div>
  }

  return (
    <div className="relative w-full">
      <Button
        size="icon"
        onClick={copyToClipboard}
        className={cn(
          "absolute top-4 right-6 p-2 bg-white hover:bg-gray-100 transition-colors rounded-full",
          isExpanded && "right-2"
        )}
        aria-label="复制代码"
      >
        {copied ? (
          <Check className="w-4 h-4 text-green-500" />
        ) : (
          <Copy className="w-4 h-4 text-black" />
        )}
      </Button>

      <SyntaxHighlighter
        language={language}
        style={oneDark}
        className={cn("w-full", isExpanded ? "h-full" : "h-[480px]")}
      >
        {code}
      </SyntaxHighlighter>

      <div className="absolute bottom-0 left-0 right-4 flex justify-center pb-2">
        <div
          className={cn(
            "backdrop-blur-sm bg-transparent p-1 w-full h-16 flex items-center justify-center",
            isExpanded && "backdrop-blur-none"
          )}
        >
          <Button
            onClick={() => setIsExpanded(!isExpanded)}
            className="bg-white hover:bg-gray-100 text-black rounded-full"
            size="sm"
          >
            {isExpanded ? "收起" : "查看全部"}
          </Button>
        </div>
      </div>
    </div>
  )
}

本地环境下,执行dev和prod命令均能正常运行,但部署到线上后,请求Server Action时出现读取文件失败的错误。


内容的提问来源于stack exchange,提问作者Ala Eddine Menai

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 14:55:57