简体   繁体   English

在 Spring Boot 2.1 MultipartFile - 启用 SSL(https)时上传文件时,几乎 50% http 400 bad request error

[英]Got almost 50% http 400 bad request error on Spring Boot 2.1 MultipartFile - file upload when enable SSL(https)

I have a API server which built with Spring Boot 2.1 on public domain which serve APIs and file upload as well.我有一个 API 服务器,它在公共域上使用 Spring Boot 2.1 构建,它也提供 API 和文件上传。

In recent days, we wants to upgrade this Spring Boot server to use SSL (https).最近几天,我们想升级这个 Spring 引导服务器以使用 SSL (https)。 Before we setup the SSL settings in Spring Boot.在我们在 Spring Boot 中设置 SSL 设置之前。 The API for file upload is workable very well (100% upload successful).用于文件上传的 API 非常好用(100% 上传成功)。

After we setup the SSL settings in Spring Boot.在我们在 Spring 引导中设置 SSL 设置之后。 The API for file upload is work but only 50% upload successful other 50% got http 400 bad request.用于文件上传的 API 有效,但只有 50% 上传成功,其他 50% 得到 http 400 错误请求。 (We sure that the problem is not related to front-end web, because we use the Swagger which bundled with Spring Boot to test can get the same result) (我们确定问题与前端 web 无关,因为我们使用 Swagger 与 Spring 捆绑在一起启动测试可以得到相同的结果)

And we lookup the server logs of Spring Boot.我们查找 Spring Boot 的服务器日志。 When the http 400 bad request happens, there were not have any logs about the http 400 bad request.当发生 http 400 bad request 时,没有任何关于 http 400 bad request 的日志。 We study many days and survey on internet but still cannot solve this problem.我们研究了很多天并在互联网上进行了调查,但仍然无法解决这个问题。 Please give helps.请给予帮助。

We already try to disable csrf (either in properties file or via config class) and many other solutions which provided on internet but still not work.我们已经尝试禁用 csrf(在属性文件中或通过配置类)和许多其他在互联网上提供但仍然无法正常工作的解决方案。

Environment: Spring Boot 2.1.13 (which is the latest version of Spring Boot 2.1)环境:Spring Boot 2.1.13(这是Spring Boot 2.1的最新版本)

Settings in properties file: (only added SSL setup section in properties file, and the SSL (https) is successfully on)属性文件中的设置:(仅在属性文件中添加了SSL设置部分,并且SSL(https)已成功打开)

# SSL setup
server.ssl.key-store=classpath:keystore.p12
server.ssl.key-store-password=abcdef
server.ssl.key-store-type=PKCS12
server.ssl.key-alias=tomcat
server.ssl.enabled-protocols=TLSv1.1,TLSv1.2
server.http2.enabled=true
security.basic.enabled=false
security.enable-csrf=false

## MULTIPART (MultipartProperties)
# Enable multipart uploads
spring.servlet.multipart.enabled = true
# Threshold after which files are written to disk.
spring.servlet.multipart.file-size-threshold=2KB
# Max file size.
spring.servlet.multipart.max-file-size=100MB
# Max Request Size
spring.servlet.multipart.max-request-size=115MB

My controller for file upload:我的 controller 用于文件上传:

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.SwaggerDefinition;
import io.swagger.annotations.Tag;
import java.util.Collections;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

@Slf4j
@RestController
@RequestMapping(value = "/v1/fileupload")
@Api(tags = {"fileupload api"}, value = "fileupload")
@SwaggerDefinition(tags = {
    @Tag(name = "fileupload api", description = "apis for file upload")
})
public class FileUploadController {

    @Autowired
    private FileUploadService fileUploadService;

    private ApiUtilHelper helper = new ApiUtilHelper();

    @ApiOperation(value = "upload single data import file")
    @RequestMapping(
        value = "/dataimport",
        method = RequestMethod.POST,,
        consumes = { MediaType.MULTIPART_FORM_DATA_VALUE },
        produces = { MediaType.APPLICATION_JSON_UTF8_VALUE }
    )
    public ResponseEntity<?> uploadSingleFileForDataImport(@RequestParam("file") MultipartFile file) throws FileStorageException {
        log.info("Enter into uploadSingleFileForDataImport");
        FileUploadResponse fileUploadResponse = fileUploadService.storeFile(file, "dataImport");
        Map<String, Object> additionals = Collections.singletonMap("filupload", fileUploadResponse);
        BasicResponse br = helper.createSuccessBaseResponse(ApiSuccessCode.CreateSuccess, additionals);
        return new ResponseEntity<BasicResponse>(br, ApiSuccessCode.CreateSuccess.getHttpStatus());
    }

Swagger test result: Swagger 测试结果:

Request URL: https://example.com:8443/v1/fileupload/dataimport
Request Method: POST
Status Code: 400 
Remote Address: 111.222.111.222:8443
Referrer Policy: no-referrer-when-downgrade

**Response http header from Spring Boot**
Connection: close
Content-Length: 0
Date: Mon, 20 Apr 2020 13:13:02 GMT

**Request http header from Swagger**
accept: application/json;charset=UTF-8
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-TW,zh;q=0.9,en-US;q=0.8,en;q=0.7,zh-CN;q=0.6,ja;q=0.5
Connection: keep-alive
Content-Length: 484098
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryiXmuHnaNthhXowmb
Cookie: _ga=GA1.2.1299976434.1580821082; JSESSIONID=2C157019D6560405CC75A5F5083DE0AE
Host: example.com:8443
Origin: https://example.com:8443
Referer: https://example.com:8443/swagger-ui.html
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.113 Safari/537.36

2020.04.20 13:44 (UTC time) supplement info as below: Thanks @nbalodi, When I setup logging.level.org.springframework.web=DEBUG. 2020.04.20 13:44(UTC 时间)补充信息如下:谢谢@nbalodi,当我设置 logging.level.org.springframework.web=DEBUG。 I got the error log now.我现在得到了错误日志。 Logs attached as below:附上的日志如下:

HttpEntityMethodProcessor : No match for [application/json;charset=UTF-8], supported: []
ExceptionHandlerExceptionResolver : Resolved [org.springframework.web.multipart.support.MissingServletRequestPartException: Required request part 'file' is not present]
DispatcherServlet : Completed 400 BAD_REQUEST

The weird part is this error situation is only happen when we use the ssl settings as I described above.奇怪的是,这种错误情况只发生在我们使用如上所述的 ssl 设置时。

Thanks everyone's reply.谢谢大家的回复。 I think this is the bug of SpringBoot or its embed Tomcat bug in SpringBoot v2.1.x version.我认为这是 SpringBoot 的错误或其在 SpringBoot v2.1.x 版本中嵌入的 Tomcat 错误。 When SpringBoot v2.3.0 formal vesion released. SpringBoot v2.3.0 正式版发布时。 I use the same code to upgrade to v2.3.0 Everything is workable now.我使用相同的代码升级到 v2.3.0 现在一切正常。 I use bulk test or called pressure test in file upload.我在文件上传中使用批量测试或称为压力测试。 It's 100% successful now.现在已经100%成功了。

Update: The root cause is Tomcat component is not stable with Spring framework - Multipart under the https and/or http2 or Spring security (such like OAuth2) enabled. Update: The root cause is Tomcat component is not stable with Spring framework - Multipart under the https and/or http2 or Spring security (such like OAuth2) enabled.

In most cases in Spring Boot 2.3.0 - 2.3.2, the fail rate of file upload is 4 ~ 14 ~ 50%.在 Spring Boot 2.3.0 - 2.3.2 大部分情况下,文件上传失败率为 4 ~ 14 ~ 50%。 After Spring Boot 2.3.5 to 2.4.0 version, the fail rate of file upload is near 50%. Spring Boot 2.3.5到2.4.0版本后,文件上传失败率接近50%。 The key different is the Tomcat version and Spring Security version is different among these Spring Boot version.关键的不同是 Tomcat 版本和 Spring 安全版本在这些 Spring 引导版本之间是不同的。

Another problem is the http2 protocol supported by Tomcat is not stable.另一个问题是Tomcat支持的http2协议不稳定。 If you enable the http2 (such like setup the server.http2.enabled=true in your properties file) and use Multipart or HttpServletRequest for your file upload, the fail rate is rise to near 90% and get connection.如果启用 http2(例如在属性文件中设置 server.http2.enabled=true)并使用 Multipart 或 HttpServletRequest 进行文件上传,则失败率上升到接近 90% 并获得连接。

Conclusion: For anyone which face the file upload unstable problem and get ERR_CONNECTION_CLOSED and find error log about结论:对于任何面临文件上传不稳定问题并获得 ERR_CONNECTION_CLOSED 并找到错误日志的人

org.apache.catalina.connector.ClientAbortException: org.apache.coyote.CloseNowException: Connection [{0}], Stream [{1}], This stream is not writable org.apache.catalina.connector.ClientAbortException: org.apache.coyote.CloseNowException: Connection [{0}], Stream [{1}], This stream is not writable

Caused by: org.apache.coyote.CloseNowException: Connection [{0}], Stream [{1}], This stream is not writable原因:org.apache.coyote.CloseNowException:连接 [{0}],Stream [{1}],此 ZF7B44CFAFD5C52223D5498196C8A2E7B 不可写

Caused by: org.apache.coyote.http2.StreamException: Connection [{0}], Stream [{1}], This stream is not writable引起:org.apache.coyote.http2.StreamException: Connection [{0}], Stream [{1}], 这个 ZF7B44CFAFD5C52223D5498196C8A2EB 不可写

You can try to upgrade to Spring Boot 2.4.0 and disable http2 support of Tomcat in Spring Boot by setup the server.http2.enabled=false in your properties file您可以尝试升级到 Spring Boot 2.4.0 并通过在属性中设置 server.http2.enabled=false 来禁用 Spring Boot 中 Tomcat 的 http2 支持

You may find the stability is improved even to 100% success rate.您可能会发现稳定性甚至提高到 100% 的成功率。 (I tried and implemented to all my clients) (我尝试并实施给我所有的客户)

If the disable http2 support is not workable for you.如果禁用 http2 支持对您不适用。 You may try to downgrade to Spring Boot 2.3.0 - 2.3.2 version or implement your file upload by HttpServletRequest (remember to disable the Multipart in Spring Boot first)您可以尝试降级到 Spring Boot 2.3.0 - 2.3.2 版本或通过 HttpServletRequest 实现文件上传(记得先禁用 Spring Boot 中的 Multipart)

You may find related developing document here and there你可以在这里那里找到相关的开发文档

Is it possible to change your log level to DEBUG?是否可以将您的日志级别更改为调试? ie logging.level.org.springframework.web=DEBUG.即 logging.level.org.springframework.web=DEBUG。

Try thism may help.试试这个可能会有所帮助。 Unable to see any other issue with your code.看不到您的代码有任何其他问题。

@RequestBody MultipartFile[] submissions

should be应该

@RequestParam("file") MultipartFile[] submissions

The files are not the request body, they are part of it and there is no built-in HttpMessageConverter that can convert the request to an array of MultiPartFile .这些文件不是请求正文,它们是它的一部分,并且没有内置的HttpMessageConverter可以将请求转换为MultiPartFile数组。

You can also use MultipartHttpServletRequest , which gives you access to the headers of the individual parts.您还可以使用MultipartHttpServletRequest ,它使您可以访问各个部分的标头。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 400(错误请求)错误 - 使用 JQuery AJAX 上传 Spring-boot 文件 - 400 (Bad Request) Error - Spring-boot File upload Using JQuery AJAX Spring AngularJS上传文件功能-400错误错误的请求 - Spring AngularJS upload file functionality - 400 error Bad request 使用 Spring 启动时出现 400 错误请求 - 400 Bad Request when using Spring Boot spring-boot 得到“400 bad request”错误,详细信息如下 - spring-boot get a “400 bad request” error, the detail is followed 发布方法 400 错误请求错误角度弹簧引导 - Post method 400 bad request error angular spring boot Spring 启动 thymeleaf 错误请求 400 而不是显示用户错误 - Spring Boot thymeleaf bad request 400 instead of showing user error 使用 Spring 启动时出现 400 错误请求 - 400 Bad Request when using Spring Boot with localDateTime Spring-Boot:400错误的请求错误,即使存在参数也是如此 - Spring-Boot : 400 Bad request error even when parameter is present 使用spring mvc:400错误请求上传jquery文件 - jquery file upload with spring mvc : 400 bad request 获取HTTP状态400 - 必需的MultipartFile参数&#39;file&#39;在spring中不存在 - Geting HTTP Status 400 - Required MultipartFile parameter 'file' is not present in spring
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM