Next.js 13中Supabase存储桶PDF文件上传失败求助
解决Next.js 13中PDF文件上传至Supabase存储桶无响应的问题
核心问题分析
你遇到的PDF上传无响应问题,主要由以下几个关键问题导致:
- 响应提前终止:
res.json("OK")写在form.parse回调外部,而form.parse是异步操作,PDF文件体积通常更大,还没完成解析就返回了响应,直接中断了解析流程。 - Supabase客户端未初始化:代码中仅导入了
createClient但未创建实例,会导致后续上传逻辑报错(虽未在问题中体现,但属于必修复的潜在问题)。 - 错误处理缺失:未捕获
form.parse和Supabase上传的错误,导致问题无法定位。 - Content-Type识别不准确:通过临时文件路径获取MIME类型,可能因临时文件无后缀导致识别错误,影响PDF文件处理。
修复后的完整代码
前端代码(优化版)
async function uploadFile(ev) { if (ev.target.files.length > 0) { const files = ev.target.files; const data = new FormData(); for (const file of files) { data.append('file', file); } try { const response = await fetch('/api/upload/', { method: "POST", body: data }); const result = await response.json(); console.log('上传结果:', result); } catch (err) { console.error('上传失败:', err); } } }
<label htmlFor="myfile" name="myfile" style={{ width: "24px", height: "24px" }}> <input type="file" id="myFile" onChange={uploadFile} accept="*"/> </label>
后端pages/api/upload.js代码
import { createClient } from '@supabase/supabase-js' import multiparty from 'multiparty'; import fs from 'fs' import mime from 'mime-types' import crypto from 'crypto' // 初始化Supabase客户端(替换为你的环境变量) const supabase = createClient( process.env.NEXT_PUBLIC_SUPABASE_URL, process.env.SUPABASE_SERVICE_ROLE_KEY ); // 禁用Next.js默认的body解析,让multiparty处理表单数据 export const config = { api: { bodyParser: false, }, }; export default async function handler(req, res) { if (req.method !== 'POST') { return res.status(405).json({ error: '仅支持POST请求' }); } const form = new multiparty.Form({ encoding: 'binary', // 明确设置二进制编码,适配PDF等二进制文件 maxFilesSize: 10 * 1024 * 1024, // 按需调整最大文件大小(示例为10MB) }); console.log("进入handler函数"); try { // 用Promise包装form.parse,避免回调嵌套并确保异步流程完成 const [fields, files] = await new Promise((resolve, reject) => { form.parse(req, (err, fields, files) => { if (err) reject(err); else resolve([fields, files]); }); }); console.log('解析到的文件:', files); if (!files.file || files.file.length === 0) { return res.status(400).json({ error: '未上传文件' }); } const file = files.file[0]; // 从原始文件名提取扩展名 const fileExtension = file.originalFilename.split('.').pop() || 'pdf'; const fileName = `${crypto.randomUUID()}.${fileExtension}`; // 读取二进制文件内容 const fileContent = fs.readFileSync(file.path); // 优先从原始文件名获取MIME类型,兜底为PDF类型 const contentType = mime.lookup(file.originalFilename) || 'application/pdf'; const { data, error } = await supabase.storage.from("mybucket") .upload(fileName, fileContent, { contentType }); if (error) { console.error('Supabase上传错误:', error); return res.status(500).json({ error: '文件上传至存储桶失败' }); } res.status(200).json({ message: '上传成功', data }); } catch (err) { console.error('解析或上传错误:', err); res.status(500).json({ error: '文件处理失败', details: err.message }); } }
关键修复说明
- 禁用默认Body解析:添加
export const config配置,禁止Next.js自动解析请求体,确保multiparty能正确处理表单数据。 - 异步化form.parse:用Promise包装解析逻辑,避免回调嵌套,同时保证所有异步操作完成后再返回响应。
- 明确二进制编码:设置
encoding: 'binary',确保multiparty正确解析PDF这类二进制文件。 - 完善错误捕获:添加全局try/catch,捕获解析、上传等环节的异常,返回明确的错误信息便于调试。
- 准确获取Content-Type:通过原始文件名获取MIME类型,避免临时文件路径导致的识别错误。
- 响应时机修正:所有操作完成后才返回响应,确保PDF文件有足够时间完成解析和上传。
内容的提问来源于stack exchange,提问作者frankBang
相关产品推荐
相关产品推荐

