Spring RestTemplate 大文件 ContentLength 自动更改
Spring RestTemplate Large Files ContentLength Auto Changed
有关于大文件 RestTemplate 的问题。是否有最大内容长度 and/or 尽管已明确设置,它是否会自动更改?
我有一个 5GB 的文件,我想通过 HTTP PUT 命令将其发送到我的 CDN (Akamai)。我已经像下面这样设置了它(以避免 java 堆 space 错误),但是我注意到当执行命令时 Content-Length 被改变了。
@Autowired
@Qualifier("restTemplateUpload")
public void setRestTemplateUpload(RestTemplate restTemplateUpload)
{
this.restTemplateUpload = restTemplateUpload;
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setBufferRequestBody(false);
restTemplateUpload.setRequestFactory(requestFactory);
}
以及发送文件的调用:
Resource resource = new FileSystemResource(localFile);
HttpHeaders headers = new HttpHeaders();
Map<String, String> headerValues = new HashMap<String, String>();
headerValues.put("X-Akamai...", value); //There are more headers, but you can assume this is correctly done as I've been able to transmit smaller files
headerValues.put("Host", host);
headers.setAll(headerValues);
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
//I've tried using this and not using this but the result is
//the same, the request sent changes the value to 1469622184
headers.setContentLength(resource.contentLength());
System.out.println(headers.getContentLength()); //Result is: 5764589480
HttpEntity<Resource> uploadRequest = new HttpEntity<Resource>(resource, headers);
response = restTemplateUpload.exchange(hostPath, HttpMethod.PUT, uploadRequest, String.class);
Charles Proxy 报告 Content-Length 1469622184 并且当请求达到该长度时连接被终止。
Java报错:
org.springframework.web.client.ResourceAccessException: I/O error on PUT request for "http://hidden.com/path/largefile.txt": too many bytes written;"
编辑:
更多观察,当我像这样硬编码 Content-Length 时:headerValues.put("Content-Length", "3764589480");
Content-Length 字段实际上没有被发送,而是 header Transfer-Encoding: chunked 被发送。
所以我 运行 进行了更多测试,结果是这样的:
硬编码内容长度:Charles 中的结果
"1064589480": 发送 Content-Length: 1064589480
"2064589480": 发送 Content-Length: 2064589480
"3064589480":删除 Content-Length,添加 Transfer-Encoding:分块
"3764589480":删除 Content-Length,添加 Transfer-Encoding:分块
"4294967295":删除 Content-Length,添加 Transfer-Encoding:分块
“4294967296”:发送 Content-Length:“0”,立即崩溃,写入的字节过多
"4764589480": 发送 Content-Length: "469622184"
"5764589480": 发送 Content-Length: "1469622184"
据我推断,在一定数量时,RestTemplate 将从 Content-Length 切换到 Transfer-Encoding。一旦它超过 4294967295,它就会开始再次将 Content-Length 返回到 0。
我对此的最佳解决方案是永远不要指定高于 4294967295 的值。如果它高于该阈值,则只需将其设置为 4294967295,RestTemplate 会将其更改为 Transfer-Encoding: chunked anyway。
是否存在某种 overflow/precision 积分问题?
据我所知,4294967295 字节是您可以在“内容长度”字段中输入的最大数量。即使它超过了这个数量,我也建议将它设置为 4294967295,因为 RestTemplate 会自动从使用 Content-Length 切换到使用 Transfer-Encoding: chunked,所以不管你放什么大小都没有关系。
public static final String MAX_TRANSFER_SIZE = "4294967295";
...
HttpHeaders headers = new HttpHeaders();
Map<String, String> headerValues = new HashMap<String, String>();
if (resource.contentLength() > Long.parseLong(MAX_TRANSFER_SIZE))
{
// This is as high as the content length can go. At this size,
// RestTemplate automatically converts from using Content-Length to
// using Transfer-Encoding:chunked.
headerValues.put("Content-Length", MAX_TRANSFER_SIZE);
}
headers.setAll(headerValues);
有关于大文件 RestTemplate 的问题。是否有最大内容长度 and/or 尽管已明确设置,它是否会自动更改?
我有一个 5GB 的文件,我想通过 HTTP PUT 命令将其发送到我的 CDN (Akamai)。我已经像下面这样设置了它(以避免 java 堆 space 错误),但是我注意到当执行命令时 Content-Length 被改变了。
@Autowired
@Qualifier("restTemplateUpload")
public void setRestTemplateUpload(RestTemplate restTemplateUpload)
{
this.restTemplateUpload = restTemplateUpload;
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setBufferRequestBody(false);
restTemplateUpload.setRequestFactory(requestFactory);
}
以及发送文件的调用:
Resource resource = new FileSystemResource(localFile);
HttpHeaders headers = new HttpHeaders();
Map<String, String> headerValues = new HashMap<String, String>();
headerValues.put("X-Akamai...", value); //There are more headers, but you can assume this is correctly done as I've been able to transmit smaller files
headerValues.put("Host", host);
headers.setAll(headerValues);
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
//I've tried using this and not using this but the result is
//the same, the request sent changes the value to 1469622184
headers.setContentLength(resource.contentLength());
System.out.println(headers.getContentLength()); //Result is: 5764589480
HttpEntity<Resource> uploadRequest = new HttpEntity<Resource>(resource, headers);
response = restTemplateUpload.exchange(hostPath, HttpMethod.PUT, uploadRequest, String.class);
Charles Proxy 报告 Content-Length 1469622184 并且当请求达到该长度时连接被终止。
Java报错:
org.springframework.web.client.ResourceAccessException: I/O error on PUT request for "http://hidden.com/path/largefile.txt": too many bytes written;"
编辑:
更多观察,当我像这样硬编码 Content-Length 时:headerValues.put("Content-Length", "3764589480"); Content-Length 字段实际上没有被发送,而是 header Transfer-Encoding: chunked 被发送。
所以我 运行 进行了更多测试,结果是这样的:
硬编码内容长度:Charles 中的结果
"1064589480": 发送 Content-Length: 1064589480
"2064589480": 发送 Content-Length: 2064589480
"3064589480":删除 Content-Length,添加 Transfer-Encoding:分块
"3764589480":删除 Content-Length,添加 Transfer-Encoding:分块
"4294967295":删除 Content-Length,添加 Transfer-Encoding:分块
“4294967296”:发送 Content-Length:“0”,立即崩溃,写入的字节过多
"4764589480": 发送 Content-Length: "469622184"
"5764589480": 发送 Content-Length: "1469622184"
据我推断,在一定数量时,RestTemplate 将从 Content-Length 切换到 Transfer-Encoding。一旦它超过 4294967295,它就会开始再次将 Content-Length 返回到 0。 我对此的最佳解决方案是永远不要指定高于 4294967295 的值。如果它高于该阈值,则只需将其设置为 4294967295,RestTemplate 会将其更改为 Transfer-Encoding: chunked anyway。 是否存在某种 overflow/precision 积分问题?
据我所知,4294967295 字节是您可以在“内容长度”字段中输入的最大数量。即使它超过了这个数量,我也建议将它设置为 4294967295,因为 RestTemplate 会自动从使用 Content-Length 切换到使用 Transfer-Encoding: chunked,所以不管你放什么大小都没有关系。
public static final String MAX_TRANSFER_SIZE = "4294967295";
...
HttpHeaders headers = new HttpHeaders();
Map<String, String> headerValues = new HashMap<String, String>();
if (resource.contentLength() > Long.parseLong(MAX_TRANSFER_SIZE))
{
// This is as high as the content length can go. At this size,
// RestTemplate automatically converts from using Content-Length to
// using Transfer-Encoding:chunked.
headerValues.put("Content-Length", MAX_TRANSFER_SIZE);
}
headers.setAll(headerValues);