NextJS 13.0.6+Supabase生成PDF在Vercel部署后损坏问题
问题排查与解决方案
核心问题分析
- 前端fetch配置错误:你在GET请求中设置了
Content-Type头并添加了duplex选项,但GET请求不存在请求体,这会触发Vercel环境下的RequestInit: duplex option is required when sending a body报错,同时可能导致响应解析异常,进而损坏PDF文件。 - 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
相关产品推荐
相关产品推荐

