背景
版本发布后,附件下载功能报错。经观察:文件大小1k以下功能正常,大于等于1k下载报错。
nginx日志显示以下报错
c
2026/03/16 11:38:16 [error] 11#0: *465 upstream sent invalid chunked response while reading upstream, client: x.x.x.x, server: localhost, request: "GET /x/x/mediaFile/download?mediaType=img&mediaId=x HTTP/1.1", upstream: "http://x.x.x.x:x/mediaFile/download?mediaType=img&mediaId=x", host: "x", referrer: "x"
定位过程
以前定位过类似问题(https://blog.csdn.net/shuxiaohua/article/details/149671002),大概知道是响应的header传的有问题。
组网如下:

测试环境复现后,在A中打印了B返回的响应头。有如下发现
当下载的文件超过1K(大于等于)后,B的响应头中包含{"Transfer-Encoding":"chunked"}
如果文件小于1k,则不包含该响应头。
翻阅代码后,大概知道了原因。
A的代码中增加了"Content-Length"响应头,tomcat会正常传输字节流(详细见之前的文章)。当文件超过1k后,B的响应头包含"Transfer-Encoding":"chunked",会被A透传给Nginx,但此时A的响应并不是按照这个编码传输的数据,因此Nginx无法解析响应而报错。
A服务问题代码
c
public void download(... HttpServletResponse servletResponse) {
# 调用B服务
Response response = PostManUtil.getStream(commonOkHttpClient, url, null, requestHeaders);
Headers responseHeaders = response.headers();
HttpHeaders headers = new HttpHeaders();
for (int i = 0; i < responseHeaders.size(); i++) {
headers.add(responseHeaders.name(i), responseHeaders.value(i));
}
# 该代码产生的问题
if (!headers.containsKey(HttpHeaders.CONTENT_LENGTH)) {
headers.setContentLength(resource.contentLength());
}
ResponseBody responseBody = response.body();
byte[] responseBytes = responseBody.bytes();
OutputStream outputStream = servletResponse.getOutputStream();
outputStream.write(responseBytes );
outputStream.flush();
}
问题解决过程
确认"Transfer-Encoding":"chunked"什么场景下产生的,看能否在不修改代码的情况下,规避问题
疑问,B服务的响应为什么超过1k后,编码就自动变成"Transfer-Encoding":"chunked"
从B服务的代码可以看到是有设置"Content-Length"头的,按照之前的源码分析,如果传了该头,tomcat是不可能采用chunked编码传输的。为了确认这一点,在dev环境断点调试tomcat相关代码,发现B服务确实不会传"Transfer-Encoding":"chunked"头。
那只有可能是ALB加的。
c
# 部分代码有省略
private void writeContent2Client(HttpServletRequest httpServletRequest, HttpServletResponse response,
String fileName, byte[] content) throws IOException {
try (OutputStream outputStream = new BufferedOutputStream(response.getOutputStream())) {
response.reset();
response.setStatus(HttpServletResponse.SC_OK);
response.addHeader("Content-Disposition",
"attachment;filename=\"" + UrlEscapers.urlFragmentEscaper().escape(fileName) + "\"");
response.addHeader(CONTENT_LENGTH, String.valueOf(content.length));
outputStream.write(content);
outputStream.flush();
}
}
ALB为什么会增加"Transfer-Encoding":"chunked"
使用千问输入"nginx 1k以上会使用chunked编码"(ALB是基于nginx),其给出了几种可能的场景。其中一种是开启了压缩。此时我想起来以前定位过相似的问题,之前找过ALB确认过他们确实有开启压缩。故咨询他们压缩配置是如何设置的。
c
gzip on;
gzip_buffers 4 16k;
gzip_comp_level 2;
gzip_min_length 1k;
gzip_http_version 1.0;
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
gzip_vary on;
通过其配置也能印证当前的现象,gzip只有在响应大小超过1k后开始进行压缩,压缩后就会自动添加"Transfer-Encoding":"chunked"头。
问题规避方案
尝试联系ALB的人确认是否能够修改压缩相关的参数,答案否。
当前问题是A服务传了"Transfer-Encoding":"chunked"头,但是并没有按照这个编码进行传输。那我们也可以开启A服务tomcat的压缩,这样就能按照chunked编码传输(开启压缩后,服务器是没办法知道压缩后的大小,也不可能压缩完了后,在整体传输,这样对于报文比较大的,会占用大量内存,所以一般开启压缩后都是使用chunked编码传输,压缩一部分然后传输一部分)。从而使nginx解码正确。A服务增加配置如下:
c
server.compression.enabled=true
server.compression.min-response-size=1024
server.compression.mime-types=text/html,text/xml,text/plain,text/css,text/javascript,application/javascript,application/json,application/xml
经验证后,问题得以解决。