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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 23:25:25