diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/file/service/object/RustfsObjectStorageService.java b/backend-java/src/main/java/com/nanri/aiimage/modules/file/service/object/RustfsObjectStorageService.java index 355bb1bf..3eab9e59 100644 --- a/backend-java/src/main/java/com/nanri/aiimage/modules/file/service/object/RustfsObjectStorageService.java +++ b/backend-java/src/main/java/com/nanri/aiimage/modules/file/service/object/RustfsObjectStorageService.java @@ -382,6 +382,22 @@ public class RustfsObjectStorageService { Math.max(1L, properties.getConnectionPoolKeepAliveMillis()), TimeUnit.MILLISECONDS)) .retryOnConnectionFailure(true) + .addNetworkInterceptor(chain -> { + okhttp3.Request request = chain.request(); + okhttp3.RequestBody body = request.body(); + // OkHttp 对「长度为 0 的请求体」不会写 Content-Length,于是请求既无 + // Content-Length 也无 Transfer-Encoding(HTTP/1.1 不允许这样)。 + // RustFS 对此直接回 411 Length Required,客户端读到不完整响应就报 + // unexpected end of stream,进而重试——线上抓包实测 90 秒内 515 次 411, + // 而上传空内容(content 为 null/空)在业务里是常态。 + // 用 network interceptor 在协议层补上该头(普通 interceptor 会被 + // BridgeInterceptor 按 body 长度覆盖掉,加了也不生效)。 + if (body != null && body.contentLength() == 0L + && request.header("Content-Length") == null) { + request = request.newBuilder().header("Content-Length", "0").build(); + } + return chain.proceed(request); + }) .build(); } return httpClient;