java Spring restTemplate execute() POST 大文件并获得响应

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/30960273/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 17:53:52  来源:igfitidea点击:

Spring restTemplate execute( ) POST large files and obtain a response

javaspringreststreamresttemplate

提问by HankCa

This took me quite a while to work out so I wanted to share it. Most information came from SO and I wanted to consolidate into this one place.

这花了我很长时间来解决,所以我想分享它。大多数信息来自 SO,我想整合到这个地方。

My requirements are to upload files using a RESTFul POST. Due to possibly large files I wanted to stream the files. I obviously want to be able to read the response.

我的要求是使用 RESTFul POST 上传文件。由于文件可能很大,我想流式传输文件。我显然希望能够阅读回复。

I planned to use Jersey as the REST Server and Spring's RestTemplate as the client (and for testing).

我计划使用 Jersey 作为 REST 服务器,使用 Spring 的 RestTemplate 作为客户端(并用于测试)。

The problem I faced was streaming POSTs and receiving a response. How can I do that? (Rhetorical question - I answer this!)

我面临的问题是流式传输 POST 并接收响应。我怎样才能做到这一点?(反问 - 我回答这个!)

采纳答案by HankCa

I am using SpringBoot 1.2.4.RELEASEwith Jersey being pulled in by:

我正在使用 SpringBoot1.2.4.RELEASE和 Jersey 被拉入:

compile("org.springframework.boot:spring-boot-starter-jersey")

I created the project with the brilliant Spring Starter Project (Spring Tool Suite > Newor you can do through a website I believe and no doubt IntelliJ has this capability also). And chose 'Jersey (JAX-RS)' option. In the gradle build.gradleI also added the dependency:

我使用出色的 Spring Starter Project 创建了该项目(Spring Tool Suite > New或者您可以通过我相信的网站来完成,毫无疑问 IntelliJ 也具有此功能)。并选择了“泽西 (JAX-RS)”选项。在gradle中,build.gradle我还添加了依赖项:

compile('commons-io:commons-io:2.4')

I wrote this server side code.

我写了这个服务器端代码。

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URI;
import java.net.URISyntaxException;

import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RestController;

import org.me.fileStore.service.FileStoreService;

@RestController
@Path("/filestore")
public class FileStoreRestService {
    private static Logger logger = LoggerFactory.getLogger(FileStoreRestService.class);

    @Autowired
    private FileStoreService fileStoreService;


    @POST
    @Path("upload")
    @Consumes(MediaType.APPLICATION_OCTET_STREAM)
    @Produces(MediaType.APPLICATION_JSON)
    public Response Upload(InputStream stream) throws IOException, URISyntaxException { //
        String location = fileStoreService.upload(stream);  // relative path
        URI loc = new URI(location);
        Response response = Response.created(loc).build();
        System.out.println("POST - response: " + response + ", :" + response.getHeaders());
        return response;
    }

Where i had most troubles was in getting a Response with a location.

我遇到最多麻烦的地方是获取带有位置的响应。

Firstly I had to handle streaming large files. I followed https://stackoverflow.com/a/15785322/1019307as you can see in the test below. I was NOT obtaining a Response no matter what I tried with the HttpMessageConverterExtractoras per that post:

首先,我必须处理流大文件。我关注了https://stackoverflow.com/a/15785322/1019307,你可以在下面的测试中看到。无论我HttpMessageConverterExtractor按照那个帖子尝试过什么,我都没有得到响应:

final HttpMessageConverterExtractor<String> responseExtractor =
new HttpMessageConverterExtractor<String>(String.class, restTemplate.getMessageConverters());

After finding https://stackoverflow.com/a/6006147/1019307I wrote:

找到https://stackoverflow.com/a/6006147/1019307 后,我写道:

private static class ResponseFromHeadersExtractor implements ResponseExtractor<ClientHttpResponse> {

    @Override
    public ClientHttpResponse extractData(ClientHttpResponse response) {
        System.out.println("StringFromHeadersExtractor - response headers: " + response.getHeaders());
        return response;
    }
}

This gave me this test:

这给了我这个测试:

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hamcrest.MatcherAssert;
import org.hamcrest.Matchers;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.test.IntegrationTest;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.boot.test.TestRestTemplate;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.web.client.RequestCallback;
import org.springframework.web.client.ResponseExtractor;
import org.springframework.web.client.RestTemplate;


@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = FileStoreApplication.class)
@WebAppConfiguration
@IntegrationTest("server.port:9000")
public class FileStoreRestServiceTest {
    private static Logger logger = LoggerFactory.getLogger(FileStoreRestServiceTest.class);
    protected final Log logger2 = LogFactory.getLog(getClass());

    String base = "http://localhost:9000/filestore";
    private RestTemplate restTemplate = new TestRestTemplate();

@Test
public void testMyMethodExecute() throws IOException {
    String content = "This is file contents\nWith another line.\n";
    Path theTestFilePath = TestingUtils.getTempPath(content);
    InputStream inputStream = Files.newInputStream(theTestFilePath);

    String url = base + "/upload";
    final RequestCallback requestCallback = new RequestCallback() {
        @Override
        public void doWithRequest(final ClientHttpRequest request) throws IOException {
            request.getHeaders().setContentType(MediaType.APPLICATION_OCTET_STREAM);
            IOUtils.copy(inputStream, request.getBody());
        }
    };
    final RestTemplate restTemplate = new RestTemplate();
    SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
    requestFactory.setBufferRequestBody(false);
    restTemplate.setRequestFactory(requestFactory);
    ClientHttpResponse response = restTemplate.execute(url, HttpMethod.POST, requestCallback,
            new ResponseFromHeadersExtractor());
    URI location = response.getHeaders().getLocation();
    System.out.println("Location: " + location);
    Assert.assertNotNull(location);
    Assert.assertNotEquals(0, location.getPath().length());

}

private static class ResponseFromHeadersExtractor implements ResponseExtractor<ClientHttpResponse> {

    @Override
    public ClientHttpResponse extractData(ClientHttpResponse response) {
        System.out.println("StringFromHeadersExtractor - response headers: " + response.getHeaders());
        return response;
    }
}

I need to refactor much in that test out into some services.

我需要在该测试中将很多内容重构为某些服务。

回答by Sotirios Delimanolis

It's unnecessary to go through all these hoops with a RequestCallback. Simply use a PathResource.

没有必要用RequestCallback. 只需使用一个PathResource.

PathResource pathResource = new PathResource(theTestFilePath);
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, new HttpEntity<>(pathResource), String.class);

Spring will use a ResourceHttpMessageConverterto serialize the file identified by the given Pathto the request body. Internally, the Spring 4.x implementation uses a buffer size of 4096 bytes (which is also what IOUtils#copy(..)uses).

Spring 将使用 aResourceHttpMessageConverter来序列化由给定Path请求体标识的文件。在内部,Spring 4.x 实现使用 4096 字节的缓冲区大小(这也是IOUtils#copy(..)使用的)。

Obviously, you can provide the response type you want. The example above expects the response body as a String. With a ResponseEntity, you can access all the response headers with

显然,您可以提供所需的响应类型。上面的示例期望响应正文为String. 使用 a ResponseEntity,您可以访问所有响应标头

HttpHeaders responseHeaders = response.getHeaders();