如何在浏览器压缩JSON并适配Node.js的bodyParser.json中间件?
嘿,我来帮你分析一下当前的实现问题,以及给出正确的做法~
现有实现的不合理之处
你的思路方向是对的(浏览器端压缩JSON再发送),但代码里有两个核心问题:
- 客户端的压缩结果使用方式不对:你写的
compressGzip函数返回的是Response对象,但fetch的body参数需要的是Blob、ReadableStream、BufferSource这类可序列化的内容,直接把Response传进去是不符合要求的。 - 服务端未处理解压逻辑:
bodyParser.json中间件默认只认明文的JSON字符串,它不会自动识别并解压Content-Encoding: gzip的请求体,所以后端会直接抛出JSON解析失败的错误。
正确的客户端实现
首先修正压缩函数,确保返回的是可以直接作为fetch body的类型(比如Blob),同时规范请求头的写法(HTTP头名称大小写不敏感,但按规范用Content-Encoding而非小写):
// 修正后的压缩函数:返回Blob类型,方便fetch使用 export async function compressGzip(inputJsonStr) { // inputJsonStr 需要是你要发送的JSON字符串(比如JSON.stringify(yourData)) const stream = new Response(inputJsonStr).body.pipeThrough(new CompressionStream('gzip')); return new Response(stream).blob(); } // React请求代码示例 async function sendCompressedData(url, data) { try { const jsonStr = JSON.stringify(data); const compressedBody = await compressGzip(jsonStr); const response = await fetch(url, { headers: { 'Content-Type': 'application/json', 'Content-Encoding': 'gzip' // 按HTTP规范写头名称 }, method: 'POST', body: compressedBody }); return response.blob(); } catch (err) { console.error('发送压缩数据失败:', err); throw err; } }
正确的服务端实现
服务端需要先添加请求体解压中间件,把gzip压缩的二进制数据解压成明文JSON,再交给bodyParser.json处理。我们可以用Node.js内置的zlib模块来实现这个中间件:
const express = require('express'); const bodyParser = require('body-parser'); const zlib = require('zlib'); const app = express(); // 自定义gzip解压中间件 app.use((req, res, next) => { const contentEncoding = req.headers['content-encoding']; // 只有当请求头明确标记gzip压缩时,才进行解压 if (contentEncoding && contentEncoding.includes('gzip')) { // 将请求流通过gunzip解压,拼接解压后的内容 let rawBody = ''; req.pipe(zlib.createGunzip()) .on('data', (chunk) => rawBody += chunk.toString()) .on('end', () => { req.body = rawBody; next(); }) .on('error', (err) => { console.error('解压请求体失败:', err); res.status(400).send('Invalid gzip data'); }); } else { // 没有压缩的请求,直接进入下一个中间件 next(); } }); // 现在bodyParser可以正常解析解压后的明文JSON了 app.use(bodyParser.json({ limit: "10mb" })); // 示例路由 app.post('/your-api', (req, res) => { console.log('解析后的JSON数据:', req.body); res.status(200).json({ message: '数据接收成功' }); }); app.listen(3000, () => { console.log('服务器运行在 http://localhost:3000'); });
额外注意事项
- 浏览器兼容性:
CompressionStream是现代浏览器才支持的API(Chrome 80+、Firefox 113+),如果需要兼容旧浏览器,可以用第三方库如pako来实现gzip压缩。 - 异常处理:服务端要处理解压失败的情况(比如客户端发送的不是合法的gzip数据),避免服务器崩溃。
- 请求头校验:可以在服务端添加逻辑,校验
Content-Encoding和Content-Type是否匹配,避免非法请求。
内容的提问来源于stack exchange,提问作者ESI
相关产品推荐
相关产品推荐

