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

NextJS 13.0.6+Supabase生成PDF在Vercel部署后损坏问题

问题排查与解决方案

核心问题分析

  1. 前端fetch配置错误:你在GET请求中设置了Content-Type头并添加了duplex选项,但GET请求不存在请求体,这会触发Vercel环境下的RequestInit: duplex option is required when sending a body报错,同时可能导致响应解析异常,进而损坏PDF文件。
  2. API端的文件路径问题:Vercel服务器仅允许写入/tmp临时目录,若getPDFFilePath返回的路径不在此目录下,会导致无法正确读取生成的PDF,最终返回损坏内容。

分步修复

1. 修复前端下载代码

移除GET请求中不必要的配置,简化代码如下:

export async function downloadAllocationPDF(allocationId: number): Promise<void> {
  const res = await fetch(`/api/pdf_generate?id=${allocationId}`, {
    method: 'GET',
    headers: {
      Accept: 'application/pdf'
    }
  })

  if (!res.ok) {
    throw new Error('PDF生成失败')
  }

  const blob = await res.blob()
  const blobUrl = window.URL.createObjectURL(blob)
  const tempLink = document.createElement('a')
  tempLink.href = blobUrl
  tempLink.setAttribute('download', `Allocation_${allocationId}.pdf`)
  tempLink.click()
  window.URL.revokeObjectURL(blobUrl) // 释放浏览器内存
}

2. 修复API端代码

确保临时文件路径正确

Vercel仅支持/tmp作为可写临时目录,修改getPDFFilePath函数:

function getPDFFilePath(allocationId: number): string {
  return `/tmp/Allocation_${allocationId}.pdf`
}

优化PDF生成与上传流程

避免直接使用流上传Supabase(流会被一次性消耗),改为先写入本地临时文件,再读取内容完成上传和返回:

import fs from 'fs/promises'

export default async (req: NextApiRequest, res: NextApiResponse) => {
  try {
    const allocationId = Number(req.query.id)
    if (!allocationId) {
      return res.status(400).json({ error: '缺少分配ID' })
    }

    // 生成PDF并写入临时文件
    const pdfPath = getPDFFilePath(allocationId)
    await createAllocationPDFToFile(data, pdfPath) // 调整生成逻辑,直接写入文件

    // 读取PDF内容
    const pdfBuffer = await fs.readFile(pdfPath)

    // 上传至Supabase存储桶
    const { error: uploadError } = await supabase.storage
      .from('files')
      .upload(`/pdf/allocations/Allocation_${allocationId}.pdf`, pdfBuffer, {
        contentType: 'application/pdf'
      })

    if (uploadError) throw uploadError

    // 返回PDF给前端
    res.setHeader('Content-Type', 'application/pdf')
    res.setHeader('Content-Disposition', `attachment; filename=Allocation_${allocationId}.pdf`)
    res.status(200).send(pdfBuffer)
  } catch (error) {
    console.error('PDF生成错误:', error)
    res.status(500).json({ error: 'PDF生成失败' })
  }
}

补充:调整PDF生成函数示例

如果原createAllocationPDF返回流,改为直接写入文件的版本(以pdf-lib为例):

import { PDFDocument } from 'pdf-lib'

async function createAllocationPDFToFile(data: any, outputPath: string) {
  const pdfDoc = await PDFDocument.create()
  // 你的PDF内容生成逻辑...
  
  const pdfBytes = await pdfDoc.save()
  await fs.writeFile(outputPath, pdfBytes)
}

额外验证点

  • 检查Supabase存储桶权限,确保上传的文件可正常访问,避免下载时获取错误内容。
  • 查看Vercel日志,确认/tmp目录读写权限正常,无相关报错。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 11:57:35