首页 > 解决方案 > Java HTTP API 调用 - 流入和流出数据

问题描述

我有一个用 Java 和 Spring Boot 编写的文件传输服务,它在没有任何本地存储的情况下将文件从源流传输到目标。

它从源获取 InputStream 的句柄,并为目标获取 OutputStream 的句柄,并有效地执行以下操作以流式传输文件。

IOUtils.copy(inputStream, outputStream);
Destination InputStream -> IOUtils.copy -> Destination OutputStream

现在我有另一个要求,我需要调用一个接受应用程序/八进制流并返回相同的加密 API。API 在客户端提供数据时对数据进行加密,同时将数据作为流返回。我已经使用以下命令使用 curl 测试了 API:

curl -v -X POST -H "Content-Type:application/octet-stream" -H "Accept:application/octet-stream" --data-binary @test-file -o test-file.enc http://127.0.0.1:1234/encrypt/test-file

现在我需要将此加密 API 与我的原始文件传输服务集成,在那里我可以获得源 InputStream 的句柄,将其提供给 API 请求,从 API 响应接收 InputStream 并将其插入到我的原始 IOUtils.copy 调用中以传输数据到目的地:

Destination InputStream -> Encryption API -> Encrypted InputStream -> IOUtils.copy -> Destination OutputStream

为了最初实现加密 API 调用,我尝试构建一个客户端并提供一个 FileInputStream 并将响应提取到 FileOutputStream 中。我已经尝试过 Apache HTTPClient 和 Spring RestTemplate。在文件较小的这两种情况下,它都按预期工作,但是对于大文件,加密 API 仅接收部分数据,并且似乎卡在那里。原始调用方(文件传输服务)也没有得到任何响应并挂起。两种实现中的行为是相同的。

使用 HTTPClient 实现:

InputStream unencryptedInputStream = new FileInputStream("test-file");
HttpClient httpclient = HttpClients.createDefault();

URIBuilder builder = new URIBuilder("http://127.0.0.1:1234/encrypt/test-file");

HttpPost request = new HttpPost(builder.build());
request.setHeader("Content-Type", "application/octet-stream");

InputStreamEntity inputStreamEntity = new InputStreamEntity(unencryptedInputStream, 1064041488);
inputStreamEntity.setContentType("application/octet-stream");
inputStreamEntity.setChunked(true);

request.setEntity(inputStreamEntity );

HttpResponse response = httpclient.execute(request);
HttpEntity entity = response.getEntity();

InputStream encryptedInputStream = entity.getContent();

OutputStream os = new FileOutputStream("test-file.enc");
IOUtils.copy(encryptedInputStream, os);

使用 Spring RestTemplate 实现:

RestTemplate restTemplate = new RestTemplate();

InputStream unencryptedInputStream = new FileInputStream("test-file");

OutputStream encryptedOutputStream = new FileOutputStream("test-file.enc");

SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setBufferRequestBody(false);
restTemplate.setRequestFactory(requestFactory);

RequestCallback requestCallback = new RequestCallback() {
    @Override
    public void doWithRequest(ClientHttpRequest clientHttpRequest) throws IOException {
        clientHttpRequest.getHeaders().add("Content-type", "application/octet-stream");
        clientHttpRequest.getHeaders().add("Accept", "application/octet-stream");

        IOUtils.copy(unencryptedInputStream, clientHttpRequest.getBody());
    }
};

ResponseExtractor<InputStream> responseExtractor = new ResponseExtractor<InputStream>() {
    @Override
    public InputStream extractData(ClientHttpResponse clientHttpResponse) throws IOException {
        IOUtils.copy(clientHttpResponse.getBody(), encryptedOutputStream);
        return clientHttpResponse.getBody();
    }
};

restTemplate.execute("http://127.0.0.1:1234/encrypt/test-file", HttpMethod.POST, requestCallback, responseExtractor);

这两种情况都在处理小文件,但挂在大文件上。我试图将请求 IOUtils.copy 包装在“可运行”线程中,但仍然是同样的问题。

标签: javaspring-boothttpclientspring-resttemplate

解决方案


推荐阅读