如何从 Apache HttpClient 4.x 通过 HttpPut 上传 InputStream

How to upload InputStream via HttpPut from Apache HttpClient 4.x

我正在尝试使用 Apache HttpClient 4.5.6 通过 HttpPut 上传 InputStream

InputStream inputStream = ..
CredentialsProvider provider = ..
HttpClient client = HttpClientBuilder.create()
                .setDefaultCredentialsProvider(provider)
                .build();

HttpPut method = new HttpPut("MY_REMOTE_URL");
InputStreamEntity entity = new InputStreamEntity(inputStream);
method.setEntity(entity);
client.execute(method);

上传需要身份验证,在我的情况下,我不知道服务器使用的是摘要身份验证还是基本身份验证,因此客户端会自动发送多个请求来确定身份验证方案。但是 InputStreamEntity 是不可重复的,导致以下错误:

org.apache.http.client.NonRepeatableRequestException: Cannot retry request with a non-repeatable request entity.

想法 #1:缓冲 InputStream

我可以用 BufferedHttpEntity 包装实体,使其可重复:

..
BufferedHttpEntity entity = new BufferedHttpEntity(new InputStreamEntity(inputStream));
method.setEntity(entity);
..

.. 但随后流首先被缓冲,然后全部发送,而不是分块/流式传输。这不适用于更大的文件。


想法 #2:抢先式身份验证

我可以添加具有正确身份验证的硬编码 header 以防止重复请求。但是正如我所说,我不知道身份验证方案。


想法 #3:上传虚拟 FileEntity 以获取上下文(我当前的解决方案)

我先上传一个空文件,获取上下文并将其添加到我的 InputStream PUT 请求中:

HttpPut testMethod = new HttpPut("MY_REMOTE_DUMMY_URL");
FileEntity testEntity = new FileEntity(testFile);
testMethod.setEntity(testEntity);
HttpContext context = new BasicHttpContext();
client.execute(testMethod, context);

// .. delete the testEntity from server ..

HttpPut method = new HttpPut("MY_REMOTE_URL");
InputStreamEntity entity = new InputStreamEntity(inputStream);
method.setEntity(entity);
client.execute(method, context);

它有效,但它看起来像是一个 hack。我应该这样做吗?有其他选择吗?

激活 expect-continue 握手。

HttpClientContext context = HttpClientContext.create();
RequestConfig config = RequestConfig.custom().setExpectContinueEnabled(true).build();
context.setRequestConfig(config);