SpringBoot中WebSocket传大文件断开是因Tomcat默认单帧缓冲区仅8KB;需通过WebServerFactoryCustomizer配置textBufferSize、binaryBufferSize及setMaxSwallowSize(-1),并确保外置Tomcat版本≥8.5,同时调优Nginx代理参数。

WebSocket本身没有协议级的单条消息长度上限,但实际能传多少,取决于你用的服务器、代理、客户端三方各自设的缓冲区和帧限制。SpringBoot默认跑在Tomcat上,而Tomcat对WebSocket文本和二进制帧各设了8192字节(即8KB)硬限制——超这个数,连接会静默关闭,前端报WebSocket is already in CLOSING or CLOSED state,服务端却可能不打日志。
Tomcat默认8KB限制在哪生效?
这个限制不是“整条消息”,而是“单帧(frame)”大小。WebSocket协议允许把大消息分片(fragmented frames)发送,但Tomcat默认不启用分片接收,它要求:如果没注册MessageHandler.Partial,就必须把整个文本/二进制消息一次性缓冲进内存再投递给你的@OnMessage方法。所以:
-
org.apache.tomcat.websocket.textBufferSize控制单帧文本数据(如JSON字符串)最大缓冲字节数,默认8192 -
org.apache.tomcat.websocket.binaryBufferSize控制单帧二进制数据(如byte[]、文件切片)最大缓冲字节数,默认也是8192 -
maxTextMessageBufferSize是另一回事:它限制“聚合后完整消息”的总大小,仅当启用分片且使用MessageHandler.Partial时才起作用,默认值-1(无限制),但前提是前两个缓冲区够大
换句话说:即使你设了maxTextMessageBufferSize=100MB,只要单帧超过8KB,Tomcat在收到第一帧就直接断连,根本不会等到第二帧。
SpringBoot里怎么改textBufferSize和binaryBufferSize?
不能写web.xml(SpringBoot没这玩意),也不能靠@Bean ServletServerContainerFactoryBean——那个只影响JSR-356标准容器行为,对内嵌Tomcat的底层帧缓冲无效。必须直接干预Tomcat的Servlet容器初始化参数:
将 Spring Boot 2.7 项目升级到 Spring Boot 3.5 的实战流程,覆盖版本基线、依赖坐标替换、Jakarta 迁移、配置兼容、异步上下文传递改造与验证门禁。用于企业多模块 Maven 项目升级与排障。
- 用
WebServerFactoryCustomizer<tomcatservletwebserverfactory></tomcatservletwebserverfactory>,在customize()里调用addConnectorCustomizers() - 关键不是改
textBufferSize,而是确保AbstractHttp11Protocol的setMaxSwallowSize(-1)也被设上——否则Tomcat会在HTTP升级阶段就把超长Upgrade请求体丢弃 - 配置项必须通过
servletContext.setInitParameter()传入,且要在应用启动早期生效;放在ServletContextInitializer里是可行的,但要注意它比WebServerFactoryCustomizer执行更晚,部分版本可能来不及
推荐写法:
@Configuration
public class WebSocketBufferConfig implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
@Value("${websocket.text.buffer.size:52428800}") // 50MB
private int textBufferSize;
@Value("${websocket.binary.buffer.size:52428800}") // 50MB
private int binaryBufferSize;
@Override
public void customize(TomcatServletWebServerFactory factory) {
factory.addConnectorCustomizers(connector -> {
ProtocolHandler handler = connector.getProtocolHandler();
if (handler instanceof AbstractHttp11Protocol) {
AbstractHttp11Protocol<?> httpProtocol = (AbstractHttp11Protocol<?>) handler;
httpProtocol.setMaxSwallowSize(-1); // 关键!防Upgrade阶段截断
httpProtocol.setProperty("textBufferSize", String.valueOf(textBufferSize));
httpProtocol.setProperty("binaryBufferSize", String.valueOf(binaryBufferSize));
}
});
}
}
为什么打成WAR包部署到外置Tomcat就失效?
因为外置Tomcat的版本行为不一致:textBufferSize和binaryBufferSize这两个参数从Tomcat 8.5开始才真正被AbstractHttp11Protocol识别并生效;Tomcat 8.0.x虽然也接受这些参数名,但实际忽略它们。如果你的生产环境还在用8.0.x,升级到8.5+或9.x是唯一可靠解法。
另外,Nginx反向代理也会加一层限制:默认client_max_body_size和proxy_buffer_size都偏小,必须显式配大,并确认proxy_set_header Upgrade $http_upgrade和proxy_set_header Connection 'upgrade'已正确设置,否则WebSocket握手都通不过。
真正容易被忽略的点是:这些缓冲区调大后,每个并发连接的内存占用会线性上升。传一个50MB文件,意味着至少要为该连接预留50MB堆内存缓冲区——别忘了JVM堆大小和GC压力。

















