Spring Boot大文件JSON请求400/413错误配置方案咨询
大文件Base64 payload通过JSON传输的问题解决
问题描述
实现了一个HTTP客户端,可发送包含Base64文件的JSON请求体,小文件传输正常,但发送20MB以上大文件时出现错误,希望尽量不使用Multipart文件上传方式。
客户端实现代码
ObjectMapper objectMapper = MappersUtils.objectMapper(); String requestBody = objectMapper .writerWithDefaultPrettyPrinter() .writeValueAsString(request); HttpRequest httpRequest = HttpRequest.newBuilder() .uri(URI.create(url + certificateName)) .POST(HttpRequest.BodyPublishers.ofString(requestBody)) .header("Content-Type", "application/json") .header("Accept", "application/json") .timeout(Duration.ofSeconds(600)) .build(); HttpClient httpClient = HttpClient.newBuilder() .version(HttpClient.Version.HTTP_2) .connectTimeout(Duration.ofSeconds(10)) .build(); CompletableFuture<HttpResponse<String>> response = httpClient.sendAsync(httpRequest, HttpResponse.BodyHandlers.ofString()); String result = response.thenApply(HttpResponse::body).get(120, TimeUnit.SECONDS); int statusCode = response.thenApply(HttpResponse::statusCode).get(120, TimeUnit.SECONDS); System.out.println(result); System.out.println(statusCode); return objectMapper.readValue(result, PdfConvertAndSignResponse.class);
Spring Boot服务端接收代码
@PostMapping( value = "/convert-and-sign/{certificateName}", consumes = {MediaType.APPLICATION_JSON_VALUE}, produces = {MediaType.APPLICATION_JSON_VALUE}) @Operation(summary = "Convert into PDF/A Level (if it's required) and sign it ") public ResponseEntity<PdfConvertAndSignResponse> convertAndSign(@RequestBody @Length(max = Integer.MAX_VALUE - 1) PdfConvertAndSignRequest request, @PathVariable String certificateName) throws PdfException { String entryPoint = getEntrypoint(ServletUriComponentsBuilder.fromCurrentRequestUri()); LocalDateTime startedAt = LocalDateTime.now(GlobalConstants.ZONE_ID); LogDto logDto = LogBuilderUtils.logDtoBuilder( request.getHeaderData().getService(), PdfConstants.WEBSERVICE, entryPoint, startedAt, HttpVerbs.POST.name()); CertificateEntity certificateEntity = certificateRepository.findByCertificateName(certificateName); if (certificateEntity != null) { String pwd = b64ToString(certificateEntity.getPassword()); PdfConvertAndSignRequestData data = MappersUtils.modelMapper().map(certificateEntity, PdfConvertAndSignRequestData.class); data.setPassword(pwd); List<PdfConvertAndSignRequestData> toProcess = new ArrayList<>(); List<PdfConvertAndSignRequestData> currentData = request.getData(); currentData.forEach(pdfSignRequestData -> { data.setPdf(pdfSignRequestData.getPdf()); toProcess.add(data); }); request.setData(toProcess); return doConvertAndSign(request, logDto); } else { throw new PdfSignRequestException("No certificate found", logDto); } }
错误信息
客户端收到响应:
{"type":"about:blank","title":"Bad Request","status":400,"detail":"Failed to read request","instance":"/pdfmanager-ws/v1/convert-and-sign/mycertname"}
状态码400,同时抛出空指针异常:
java.lang.NullPointerException: Cannot invoke "java.util.List.get(int)" because the return value of "fr.dsidiff.pdf.client.model.response.PdfConvertAndSignResponse.getData()" is null
服务器access日志:
192.168.0.1 - - [05/Jul/2023:14:49:32 +0200] "POST /pdfmanager-ws/v1/convert-and-sign/mycertname HTTP/1.1" 400 161 192.168.0.1 - - [05/Jul/2023:14:49:47 +0200] "POST /pdfmanager-ws/v1/convert-and-sign/mycertname HTTP/1.1" 400 161
开启HTTP/2后出现413 – Payload Too Large错误。
现有配置
Tomcat server.xml配置
<Connector port="8090" protocol="HTTP/1.1" maxThreads="150" connectionTimeout="-1" maxPostSize="109951162777600" maxSwallowSize="109951162777600" redirectPort="8443" />
Spring Boot application.properties配置
# maximum size of the request body/payload server.tomcat.max-swallow-size=200MB spring.servlet.multipart.max-file-size=200MB # maximum size of entire POST request server.tomcat.max-http-form-post-size=200MB spring.servlet.multipart.max-request-size=200MB
解决方案
1. 调整Spring Boot核心请求体限制
spring.servlet.multipart系列配置仅针对Multipart请求,JSON请求体需要单独配置:
# 全局HTTP请求体大小限制(覆盖所有POST请求类型) server.max-http-request-size=200MB # Jackson解析器最大字符串长度(适配Base64大文件) spring.jackson.parser.max-string-length=273066667 # 对应200MB原文件的Base64编码长度(原文件*4/3)
2. 适配HTTP/2的Tomcat配置
开启HTTP/2后需要调整帧大小和并发流限制:
server.http2.enabled=true # 调大HTTP/2帧大小(默认16KB,建议设为64KB) server.tomcat.http2.max-frame-size=65536 # 增加并发流数量 server.tomcat.http2.max-concurrent-streams=100
3. 客户端代码优化(避免大内存占用)
不要一次性将Base64大文件转为字符串,改用流式传输减少内存压力:
ObjectMapper objectMapper = MappersUtils.objectMapper(); HttpRequest httpRequest = HttpRequest.newBuilder() .uri(URI.create(url + certificateName)) // 用流式生成请求体,避免大字符串占用内存 .POST(HttpRequest.BodyPublishers.ofInputStream(() -> { try { return new ByteArrayInputStream(objectMapper.writeValueAsBytes(request)); } catch (JsonProcessingException e) { throw new UncheckedIOException(e); } })) .header("Content-Type", "application/json") .header("Accept", "application/json") .timeout(Duration.ofSeconds(600)) .build(); HttpClient httpClient = HttpClient.newBuilder() .version(HttpClient.Version.HTTP_2) .connectTimeout(Duration.ofSeconds(10)) .build(); // 流式读取响应,避免大字符串加载到内存 CompletableFuture<HttpResponse<InputStream>> response = httpClient.sendAsync(httpRequest, HttpResponse.BodyHandlers.ofInputStream()); try (InputStream in = response.thenApply(HttpResponse::body).get(120, TimeUnit.SECONDS)) { PdfConvertAndSignResponse result = objectMapper.readValue(in, PdfConvertAndSignResponse.class); int statusCode = response.thenApply(HttpResponse::statusCode).get(120, TimeUnit.SECONDS); System.out.println(statusCode); return result; }
4. 服务端代码健壮性优化
添加请求体非空校验,避免解析失败时触发空指针:
public ResponseEntity<PdfConvertAndSignResponse> convertAndSign(@RequestBody @Length(max = Integer.MAX_VALUE - 1) PdfConvertAndSignRequest request, @PathVariable String certificateName) throws PdfException { // 新增请求数据校验 if (request == null || request.getData() == null || request.getData().isEmpty()) { LogDto errorLog = LogBuilderUtils.logDtoBuilder( null, PdfConstants.WEBSERVICE, getEntrypoint(ServletUriComponentsBuilder.fromCurrentRequestUri()), LocalDateTime.now(GlobalConstants.ZONE_ID), HttpVerbs.POST.name()); throw new PdfSignRequestException("Invalid or empty request data", errorLog); } // 原有业务代码... }
内容的提问来源于stack exchange,提问作者davidvera
相关产品推荐
相关产品推荐

