如何在NextJS中代理文件上传至外部Node/Express服务器?
问题
我正在使用NextJS和外部Node/Express服务器处理文件上传。直接从浏览器向外部服务器上传文件一切正常,但我希望通过NextJS代理完成上传操作。以下是我尝试的代码(contract为存储文件的字段名):
NextJS API路由代码:
export default async function handler(req, res) { const { companyId, investmentId } = req.query const queryString = req.url.split('?').pop() const { user } = await unstable_getServerSession(req, res, authOptions) const investment = await Investment.findOne({ _id: investmentId, company: companyId }) if (!investment || investment.owner !== user._id) { return res.status(403).send('forbidden') } const url = 'http://localhost:3001/upload/contract?' + queryString // const url = 'https://contracts.citrus.fund/upload?' + queryString console.log(url) const response = await axios.post(url, { contract: req.body, }) console.log(response.data) res.send({ ok: true }) }
请求能到达外部服务器,但multer无法将其识别为文件。外部Express服务器的相关代码如下:
const storage = multer.diskStorage({ destination: async (req, file, cb) => { const directory = path.join(process.cwd(), 'documents', req.query.companyId) console.log({ directory }) await mkdirp(directory) cb(null, directory) }, filename: (req, file, cb) => { const fileName = 'c-' + req.query.investmentId + '.pdf' console.log({ fileName }) cb(null, fileName) }, }) const uploader = multer({ storage }) router.post('/contract', uploader.single('contract'), (req, res) => { const { companyId, investmentId } = req.query res.send({ url: `/documents/${companyId}/c-${investmentId}.pdf` }) })
请问我需要修改NextJS中的handler代码,才能让文件正确到达外部服务器并被multer识别?
解决方案
问题出在你没有正确转发multipart/form-data格式的请求,multer仅能识别这种格式的文件上传。需要做以下修改:
1. 安装依赖
首先安装formidable,用于解析NextJS API路由中收到的multipart请求:
npm install formidable # 或者 yarn add formidable
2. 修改NextJS API路由代码
禁用默认的body解析器,用formidable解析请求体,再用FormData格式转发给外部服务器:
import formidable from 'formidable'; import axios from 'axios'; // 禁用NextJS默认的body解析,因为要处理multipart/form-data export const config = { api: { bodyParser: false, }, }; export default async function handler(req, res) { const { companyId, investmentId } = req.query; const queryString = req.url.split('?').pop(); const { user } = await unstable_getServerSession(req, res, authOptions); const investment = await Investment.findOne({ _id: investmentId, company: companyId }); if (!investment || investment.owner !== user._id) { return res.status(403).send('forbidden'); } try { // 解析客户端传来的multipart/form-data请求 const form = formidable({}); const [fields, files] = await form.parse(req); // 创建新的FormData用于转发到外部服务器 const formData = new FormData(); // formidable解析出的文件是数组,取第一个元素 formData.append('contract', files.contract[0], files.contract[0].originalFilename); const url = 'http://localhost:3001/upload/contract?' + queryString; const response = await axios.post(url, formData, { // 让axios自动设置正确的Content-Type(包含boundary) headers: { ...formData.getHeaders(), }, }); console.log(response.data); res.send({ ok: true }); } catch (error) { console.error('上传失败:', error); res.status(500).send('上传失败'); } }
关键说明
- NextJS默认的API路由不会自动解析
multipart/form-data,所以必须禁用bodyParser,用formidable手动解析 - 之前直接把
req.body作为JSON发送,multer无法识别这种格式的文件,必须用FormData格式转发 - 发送请求时不要手动设置
Content-Type: multipart/form-data,formData.getHeaders()会自动生成包含正确分隔符(boundary)的请求头,这是multer识别文件的关键
内容的提问来源于stack exchange,提问作者Pastilhas
相关产品推荐
相关产品推荐

