为何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.
相关产品推荐
相关产品推荐

