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

为何ExpressJS大文件上传路由会阻塞所有其他路由?

问题原因分析

你遇到的问题核心在于Node.js单线程事件循环的工作机制,以及当前multer配置的行为:

  • Express的请求体解析(包括文件上传)是在你的路由处理函数执行前完成的,multer的memoryStorage会把整个上传文件加载到内存中,这个过程是同步阻塞的——Node.js需要把HTTP请求的所有数据块拼接成完整的文件存入内存,这个操作会占用事件循环的同步执行阶段,导致其他所有请求都必须排队等待这个操作完成,所以你的console.log要等上传完才会输出,其他路由也无法响应。
  • 哪怕是发送请求到非上传路由,只要事件循环被当前的同步解析操作占用,新请求的回调就无法被执行,自然会出现阻塞。
解决方案

1. 改用磁盘存储替代内存存储

multer的diskStorage会将上传的文件边接收边异步写入磁盘,不会把整个文件加载到内存,这样事件循环不会被长时间占用,其他请求可以正常处理:

import express from 'express'
import {asyncHandler} from '../../helpers/asyncHandler'
import multer from 'multer'
import path from 'path'
import fs from 'fs'

const adminRoute = express.Router()
// 确保上传目录存在,不存在则创建
const uploadDir = path.join(__dirname, '../uploads')
if (!fs.existsSync(uploadDir)) {
  fs.mkdirSync(uploadDir, { recursive: true })
}

const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, uploadDir)
  },
  filename: (req, file, cb) => {
    // 生成唯一文件名避免冲突
    cb(null, `${Date.now()}-${file.originalname}`)
  }
})

const upload = multer({ storage: storage })

adminRoute.post('/UploadAddressesXlsx', upload.any(), asyncHandler(async (req, res) => {
  console.log('started handling file upload')
  res.status(200).type('json').send('finished')
}))

export default adminRoute

2. 限制上传文件大小

通过multer的limits配置限制最大文件尺寸,避免超大文件长时间占用事件循环:

const upload = multer({ 
  storage: storage,
  limits: { 
    fileSize: 10 * 1024 * 1024 // 限制为10MB,可根据需求调整
  }
})

3. 流式处理上传(无需全量接收文件)

如果不需要将文件完整存储,而是要实时处理(比如解析Excel内容),可以用流式处理工具(如busboy,multer底层依赖它)直接处理文件流,这样请求一进入路由就能执行你的代码,且不会阻塞事件循环:

import express from 'express'
import busboy from 'busboy'

const adminRoute = express.Router()

adminRoute.post('/UploadAddressesXlsx', (req, res) => {
  console.log('started handling file upload')
  const bb = busboy({ headers: req.headers })
  
  bb.on('file', (fieldname, fileStream, fileInfo) => {
    // 这里可以直接处理文件流,比如pipe到Excel解析工具
    // 示例:如果不需要存储,调用resume()避免内存泄漏
    fileStream.resume()
  })

  bb.on('finish', () => {
    res.status(200).type('json').send('finished')
  })

  req.pipe(bb)
})

export default adminRoute

内容的提问来源于stack exchange,提问作者Nir O.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 06:27:41