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

Node.js文件上传中间件阻塞求助:文件已保存但请求无法推进

Node.js + Express + multiparty 文件上传问题解决方案

问题描述

  • 文件已成功保存到uploads目录,但请求无法从上传中间件进入后续路由处理函数
  • 若将next()移到form.parse回调外部,请求可推进但req.body中无文件数据

问题原因

  1. req.body未初始化导致报错:Express的req.body默认值为undefined,直接执行req.body.uploadedFile = uploadedFile会抛出TypeError,中断回调内代码执行,导致next()无法被调用
  2. 若将next()放在form.parse外部,会在multiparty完成文件解析前就推进请求,此时文件数据尚未写入req.body,因此后续路由无法获取数据

解决方案

1. 修复中间件中的req.body初始化问题

修改uploadFileMiddleware,在给req.body赋值前先初始化对象:

// fileUpload.ts
import multiparty from 'multiparty';
import { Request, Response, NextFunction } from 'express';
import fs from 'fs';
import { InternalServerError } from '../utils/response_handlers/error_categories';

const uploadDir = 'uploads';

if (!fs.existsSync(uploadDir)) {
    fs.mkdirSync(uploadDir, { recursive: true });
}

export function uploadFileMiddleware(req: Request, res: Response, next: NextFunction) {
    console.log('Media SERVICE: /upload route hit');

    const form = new multiparty.Form({
        uploadDir,
    });

    form.parse(req, (err, fields, files) => {
        if (err) {
            console.error('Error parsing form:', err);
            return next(new InternalServerError());
        }

        console.log('FIELDS:', fields);
        console.log('FILES:', files);

        const uploadedFile = files.file?.[0];
        if (!uploadedFile) {
            return next(new InternalServerError('No file uploaded'));
        }

        console.log('FILE RECEIVED:');
        console.log('Original Name:', uploadedFile.originalFilename);
        console.log('MIME Type:', uploadedFile.headers['content-type']);
        console.log('File Size:', uploadedFile.size);
        console.log('Stored at:', uploadedFile.path);

        // 先初始化req.body为对象,避免undefined赋值报错
        req.body = req.body || {};
        req.body.uploadedFile = uploadedFile;

        // 调用next推进请求到后续路由
        next();
    });
}

2. 确保上传路由不受默认body解析中间件干扰

如果你的Express应用全局使用了express.json()或express.urlencoded(),需要确保上传路由跳过这些中间件——它们会消耗请求流,导致multiparty无法解析文件:

// 应用入口示例
import express from 'express';
import mediaRouter from './routes/media';

const app = express();

// 全局使用body解析中间件,但排除上传路由
app.use(/^(?!\/media\/upload).*$/, express.json());
app.use(/^(?!\/media\/upload).*$/, express.urlencoded({ extended: true }));

// 挂载媒体路由
app.use('/media', mediaRouter);

3. 可选:添加全局错误捕获逻辑

在回调内添加try-catch,避免其他未预见的错误导致next()无法执行:

form.parse(req, (err, fields, files) => {
    try {
        if (err) {
            console.error('Error parsing form:', err);
            return next(new InternalServerError());
        }

        console.log('FIELDS:', fields);
        console.log('FILES:', files);

        const uploadedFile = files.file?.[0];
        if (!uploadedFile) {
            return next(new InternalServerError('No file uploaded'));
        }

        console.log('FILE RECEIVED:');
        console.log('Original Name:', uploadedFile.originalFilename);
        console.log('MIME Type:', uploadedFile.headers['content-type']);
        console.log('File Size:', uploadedFile.size);
        console.log('Stored at:', uploadedFile.path);

        req.body = req.body || {};
        req.body.uploadedFile = uploadedFile;

        next();
    } catch (error) {
        console.error('Middleware error:', error);
        next(new InternalServerError('File upload middleware failed'));
    }
});

内容的提问来源于stack exchange,提问作者Deepak Singh

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 05:28:16