当前位置: 首页 > 知识库问答 >
问题:

通过@Bean提供的RestTemplateBuilder缓冲完整文件进行流式上传

慕阳
2023-03-14

我正在构建一个反向代理,用于上传大文件(数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\u TEMPLATE\u MODE值控制它是否满足我的流媒体要求。

问题:为什么REST\u TEMPLATE\u MODE==3会导致完全文件缓冲?

参考资料:

  • 如何使用RestTemplate转发大文件
  • 如何使用restTemplate Spring mvc发送多部分表单数据
  • Spring—如何将大型多部分文件上传到数据库而不存储在本地文件系统上—建立InputStream
  • 如何使用批注自动关联RestTemplate
  • 设计说明和使用注意事项,另外:restemplate不支持流式下载

共有1个答案

白星腾
2023-03-14

简而言之,Spring Boot提供的RestTemplateBuilder作为@Bean提供的实例包括与执行器/度量相关联的拦截器(过滤器)-拦截器接口需要将请求主体缓冲为简单的字节[]

如果您从头开始实例化自己的restemplatebuilderrestemplate,则默认情况下不会包含此项。

我似乎是唯一访问此帖子的人,但为了在我发布完整的解决方案之前帮助他人,我发现了一个重要线索:

restTemplate.getInterceptors().forEach(item->System.out.println(item));

显示。。。

org.SF.boot.actuate.metrics.web.client.MetricsClientHttpRequestInterceptor

如果我通过setInterceptors清除拦截器列表,问题就解决了。此外,我发现任何拦截器,即使它只执行NOP,也会引入完整的文件缓冲。

我已经显式地设置了缓冲区请求体=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);
    }
}

这表明,如果拦截器列表不为空,则使用InterceptingClientHttp刚需工厂

/**
 * 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需要将body缓冲到字节[]。没有使用流媒体接口的选项。

    @Override
    public ClientHttpResponse execute(HttpRequest request, byte[] body) throws IOException {
 类似资料:
  • 我也有这里提到的同样的问题。 然后我想把它作为一个上传体流到另一个服务endpoint。 该解决方案的生成方式如下:bodytype=org.springframework.web.reactive.function.bodyinserters不支持

  • 我正在阅读有关流的信息,发现我们可以使用setvbuf()函数来控制流......它写的是在行缓冲模式中,当遇到换行符时流将数据发送到文件中,在无缓冲状态下没有缓冲......所以我写了以下代码...... 所以我认为,因为这些是无缓冲流,所以输入应该在我写入屏幕后立即发送到标准输出。。。但程序在写入每一行后等待我按enter键,然后屏幕上只显示输出(由于fwrite)。。。我的问题是,当这些是无

  • 问题内容: 我正在为Django使用apache + mod_wsgi。 并且所有css / js / images都通过提供。 出于某种奇怪的原因,当其他人/朋友/同事尝试访问该网站时,jquery / css不会为他们加载,因此页面看上去很混乱。 我的html文件使用这样的代码- 我的nginx配置是这样的 有一个目录,其中有相应的&目录。 奇怪的是,当我访问它们时页面显示正常。 我已经清除了

  • 我已经找了几个小时来解决我的问题。首先我有高清7800系列amd GPU,最新的驱动程序。 我正在尝试创建一个framebuffer类,它拥有我需要的所有framebuffer函数。看起来我让renderbuffers开始工作了,但绘制到纹理会导致非常奇怪的错误。 GLCHECKFRAMEBERSTATUS在两次检查时返回GL_FRAMEBUFFER_Completed_附件。createDept

  • 本文向大家介绍通过Ajax方式上传文件使用FormData进行Ajax请求,包括了通过Ajax方式上传文件使用FormData进行Ajax请求的使用技巧和注意事项,需要的朋友参考一下 通过传统的form表单提交的方式上传文件: Html代码  不过传统的form表单提交会导致页面刷新,但是在有些情况下,我们不希望页面被刷新,这种时候我们都是使用Ajax的方式进行请求的: Js代码  如上,通过$(

  • id=“root” XML的一个示例 代码 //SiebelMessage[@id='root'] 你知道我做错了什么吗?