现象 → 代理配置修复 → 原理剖析 → Content-Length 机制详解
对于同一个文件,通过接口来获取二进制文件流的时候,线上部署环境是 ok 的,可以正常下载,但是本地的 vite proxy 代理对于同一个文件展示不同的效果,会直接拒绝访问,报错信息为:
(failed)
net::ERR_INVALID_HTTP_RESPONSE
configure: (proxy, options) => {
proxy.on('proxyRes', (proxyRes, req, res) => {
// 检测二进制流类型的响应
if (proxyRes.headers['content-type'] &&
(proxyRes.headers['content-type'].includes('application/octet-stream') ||
proxyRes.headers['content-type'].includes('application/pdf') ||
proxyRes.headers['content-type'].includes('image/'))) {
// 删除 Content-Length 头
delete proxyRes.headers['content-length'];
}
});
}
在 vite.config 的 proxy 配置中加入 configure 钩子,监听 proxyRes 事件,对二进制类型响应删除 content-length 头即可。
Vite 的代理功能基于 http-proxy 库实现,其默认配置会对响应做一些"优化处理",比如:
这些处理对普通 JSON / 文本响应没问题,但对二进制流(如文件、图片、PDF 等)会造成严重问题:
上面这段配置的作用是:
对于二进制流,正确的传输方式通常是:
Vite 的默认代理配置没有针对二进制流做特殊处理,而我们的自定义配置正是修复了这个问题,让二进制数据能够原封不动地从后端传输到前端,从而与线上环境保持一致的行为。
Content-Length 的工作流程涉及客户端与服务器之间的"长度协商",核心是"客户端告知服务器自己要发多少数据"和"服务器告知客户端会收到多少数据",两者是独立的。
当客户端向服务器发送数据时(如 POST 表单、上传文件),可能会在请求头中携带 Content-Length,用于告知服务器:"我接下来要发的数据总共有 X 字节,请准备接收这么多"。例如:
POST /upload HTTP/1.1
Host: example.com
Content-Length: 1024 # 客户端告知:本次请求体有1024字节
Content-Type: application/octet-stream
[1024字节的二进制数据]
当服务器向客户端返回数据时(如返回文件、接口响应),会在响应头中携带 Content-Length,用于告知客户端:"我接下来要返回的数据总共有 Y 字节,请准备接收这么多"。例如:
HTTP/1.1 200 OK
Content-Length: 2048 # 服务器告知:本次响应体有2048字节
Content-Type: application/pdf
[2048字节的PDF二进制数据]
此时客户端(浏览器)会根据这个值来判断是否已接收完整数据(如果实际收到的字节数 ≠ Content-Length,会判定为"响应无效",触发 ERR_INVALID_HTTP_RESPONSE 等错误)。
问题出在服务器 → 客户端的响应阶段,且与 Vite 代理(基于 http-proxy)的处理逻辑相关: