我正在构建一个用于上传大文件(数 GB)的反向代理,因此希望使用不缓冲整个文件的流模型。大缓冲区会引入延迟,更重要的是,它们可能会导致内存不足错误。
我的客户类包含
@Autowired private RestTemplate restTemplate;
@Bean
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
int REST_TEMPLATE_MODE = 1; // 1=streams, 2=streams, 3=buffers
return
REST_TEMPLATE_MODE == 1 ? new RestTemplate() :
REST_TEMPLATE_MODE == 2 ? (new RestTemplateBuilder()).build() :
REST_TEMPLATE_MODE == 3 ? restTemplateBuilder.build() : null;
}
和
public void upload_via_streaming(InputStream inputStream, String originalname) {
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setBufferRequestBody(false);
restTemplate.setRequestFactory(requestFactory);
InputStreamResource inputStreamResource = new InputStreamResource(inputStream) {
@Override public String getFilename() { return originalname; }
@Override public long contentLength() { return -1; }
};
MultiValueMap<String, Object> body = new LinkedMultiValueMap<String, Object>();
body.add("myfile", inputStreamResource);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body,headers);
String response = restTemplate.postForObject(UPLOAD_URL, requestEntity, String.class);
System.out.println("response: "+response);
}
这是有效的,但请注意我的
REST_TEMPLATE_MODE
value 控制它是否满足我的流媒体要求。问题 : 为什么
REST_TEMPLATE_MODE == 3
导致全文件缓冲?引用:
restTemplate
does not support streaming downloads 最佳答案
简而言之,RestTemplateBuilder
的实例作为 @Bean
提供Spring Boot 包含一个与执行器/指标相关联的拦截器(过滤器)——并且拦截器接口(interface)需要将请求主体缓冲为一个简单的 byte[]
.
如果你实例化你自己的 RestTemplateBuilder
或 RestTemplate
从头开始,默认情况下它不会包含此内容。
我似乎是唯一一个访问这篇文章的人,但以防万一它在我发布完整的解决方案之前对某人有所帮助,我发现了一个很大的线索:
restTemplate.getInterceptors().forEach(item->System.out.println(item));
显示...
org.SF.boot.actuate.metrics.web.client.MetricsClientHttpRequestInterceptor
如果我通过
setInterceptors
清除拦截器列表,它解决了问题。此外,我发现任何拦截器,即使它只执行 NOP,也会引入全文件缓冲。公共(public)类 SimpleClientHttpRequestFactory { ...
我已明确设置
bufferRequestBody = false
,但显然如果使用拦截器,则绕过此代码。早点知道就好了……@Override
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
HttpURLConnection connection = openConnection(uri.toURL(), this.proxy);
prepareConnection(connection, httpMethod.name());
if (this.bufferRequestBody) {
return new SimpleBufferingClientHttpRequest(connection, this.outputStreaming);
}
else {
return new SimpleStreamingClientHttpRequest(connection, this.chunkSize, this.outputStreaming);
}
}
公共(public)抽象类 InterceptingHttpAccessor 扩展 HttpAccessor { ...
这表明
InterceptingClientHttpRequestFactory
如果列表 interceptors
则使用不是空的。/**
* Overridden to expose an {@link InterceptingClientHttpRequestFactory}
* if necessary.
* @see #getInterceptors()
*/
@Override
public ClientHttpRequestFactory getRequestFactory() {
List<ClientHttpRequestInterceptor> interceptors = getInterceptors();
if (!CollectionUtils.isEmpty(interceptors)) {
ClientHttpRequestFactory factory = this.interceptingRequestFactory;
if (factory == null) {
factory = new InterceptingClientHttpRequestFactory(super.getRequestFactory(), interceptors);
this.interceptingRequestFactory = factory;
}
return factory;
}
else {
return super.getRequestFactory();
}
}
类 InterceptingClientHttpRequest 扩展 AbstractBufferingClientHttpRequest { ...
接口(interface)清楚地表明使用
InterceptingClientHttpRequest
需要缓冲 body
到 byte[]
.没有使用流接口(interface)的选项。 @Override
public ClientHttpResponse execute(HttpRequest request, byte[] body) throws IOException {
关于spring - 通过@Bean 提供的 RestTemplateBuilder 流式上传缓冲完整文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51433306/