使用express.raw()处理文件上传时数据被截断的问题排查
问题
我正在运行Express.js 4.18.2,有一个用于存储二进制文件的路由。使用express.raw中间件时,生成的Buffer被截断——比如上传5k的测试文件,PUT路由中得到的Buffer的byteLength仅为1791字节。相关代码如下:
主应用代码
import createError from 'http-errors'; import express from 'express'; import path from 'path'; import cookieParser from 'cookie-parser'; import logger from 'morgan'; import status from 'http-status'; import storageObjectsRouter from './routes/stored-objects'; import { Request, Response, NextFunction } from 'express'; import fs from 'fs'; import { HttpError } from 'http-errors'; import mung from 'express-mung'; import cors from 'cors'; const port = 3000; const app = express(); const dataDir = path.join(__dirname, 'data'); if (!fs.existsSync(dataDir)) { fs.mkdirSync(dataDir); } app.set('dataDir', dataDir); // view engine setup app.set('views', path.join(__dirname, 'views')); app.set('view engine', 'pug'); app.use(cors()); app.use( express.raw({ type: (req) => { const contentType = req.headers['content-type']; console.debug(`Got content type ${contentType}`); if ( contentType !== 'application/json' && !contentType?.startsWith('text/') ) { return true; } return false; }, limit: '1GB', }) ); app.use( mung.json((body, _req, res) => { console.debug('Munging json()'); if (res.statusCode < 300) { body = { result: body }; } return body; }) ); app.use(logger('dev')); app.use(express.json()); app.use(express.urlencoded({ extended: false })); app.use(cookieParser()); app.use(express.static(path.join(__dirname, 'public'))); app.use('/v1/stored-objects', storageObjectsRouter); // catch 404 and forward to error handler app.use(function (_req, _res, next) { next(createError(404)); }); // error handler app.use(function ( err: HttpError, req: Request, res: Response, _next: NextFunction ) { // set locals, only providing error in development res.locals.message = err.message; res.locals.error = err; const statusCode = err.status || 500; // render the error page res.status(statusCode).json({ error: { statusCode, cause: status[statusCode], message: err.message || status[`${statusCode}_MESSAGE`], }, }); }); app.listen(port, 'localhost', () => { console.log(`Listening on localhost:${port}`); });
路由处理程序(/stored-objects)
import express from 'express'; const router = express.Router(); router.put('/:id', (req, res, next) => { console.debug(`PUT stored-objects`, { params: req.params }); if (req.body instanceof Buffer) { console.debug( 'IN PUT route, buffer size - no shit - ', req.body.byteLength ); } res.json({"status":"OK"}); });
解决方法
问题出在中间件的加载顺序上:
- 你先加载了
express.raw,但之后又加载了express.json()中间件。当请求进入时,express.raw已经把请求体解析成Buffer,但后续的express.json()会尝试重新解析这个Buffer——由于二进制数据不是合法JSON,解析失败后会把req.body重置为无效或截断后的数据。
修复步骤:
- 调整中间件顺序,把
express.json()和express.urlencoded()移到express.raw之前。这样JSON类型的请求会被express.json()优先处理,二进制请求则跳过JSON解析,留给express.raw处理。 - 确保
express.raw的type判断逻辑正确,避免和其他解析中间件的处理范围重叠。
修改后的主应用中间件顺序示例:
app.use(cors()); app.use(logger('dev')); app.use(express.json()); app.use(express.urlencoded({ extended: false })); app.use( express.raw({ type: (req) => { const contentType = req.headers['content-type']; console.debug(`Got content type ${contentType}`); if ( contentType !== 'application/json' && !contentType?.startsWith('text/') ) { return true; } return false; }, limit: '1GB', }) ); app.use( mung.json((body, _req, res) => { console.debug('Munging json()'); if (res.statusCode < 300) { body = { result: body }; } return body; }) ); app.use(cookieParser()); app.use(express.static(path.join(__dirname, 'public')));
注:mung.json()仅处理JSON响应,不会影响请求体解析,所以位置放在express.raw之后不影响结果。
内容的提问来源于stack exchange,提问作者Aurelia Peters
相关产品推荐
相关产品推荐

