问题
I am using apache async http client to stream objects from azure storage.
I only need to return the HttpResponse object which has the stream associated. My clients will actually have to read from that stream to store the file locally.
So Apache Async clients use a BasicAsyncResponseConsumer which actually buffers the entire file in local memory before calling the completed callback.
I am trying to create my own implementation of AbstractAsyncResponseConsumer so that I can stream the response body instead of actually storing it first but have been unsuccessful to do so till now.
Here is the bare bones cosumer class for reference ->
public class MyConsumer extends` AbstractAsyncResponseConsumer<HttpResponse> {
@Override
protected void onResponseReceived(HttpResponse response) throws HttpException, IOException {
}
@Override
protected void onContentReceived(ContentDecoder decoder, IOControl ioctrl) throws IOException {
}
@Override
protected void onEntityEnclosed(HttpEntity entity, ContentType contentType) throws IOException {
}
@Override
protected HttpResponse buildResult(HttpContext context) throws Exception {
return null;
}
@Override
protected void releaseResources() {
}
}
And here is the code to send the request and return the response ->
public void getFile(HttpRequestBase request) {
MyConsumer myConsumer = new MyConsumer();
HttpAsyncRequestProducer producer =
HttpAsyncMethods.create(request);
CompletableFuture<HttpResponse> future = new CompletableFuture<>();
return Future<HttpResponse> responseFuture =
httpclient.execute(producer,myConsumer,
new FutureCallback<HttpResponse>() {
@Override
public void completed(HttpResponse result) {
//This is called only when all the response body has been read
//future.complete(Result)
}
@Override
public void failed(Exception ex) {
}
@Override
public void cancelled() {
}
});
return future;
}
I will be returning a CompletableFuture of the HttpResponse object to my clients.
They shouldnt be waiting for my http client to read all the response body first in local buffer.
They ideally should start copying directly from the stream provided in the response object.
What should I add inmy implementation of the consumer to get the desired result ?
回答1:
I don't know if you still have this problem, but if what you want is an InputStream
that actually streams data, then you'll want to use the blocking version of Apache HttpClient.
Java's built-in InputStream
and OutputStream
are inherently blocking, so returning a CompletableFuture
of InputStream
essentially defeats the purpose. BasicAsyncResponseConsumer
buffering the entire response in memory is actually the right thing to do, because that's the only way of making it truly non-blocking.
Another option you can take a look at is HttpAsyncMethods.createZeroCopyConsumer
. What it does is that it stores the content to a file in a completely non-blocking way.
Here's an example:
try (CloseableHttpAsyncClient client = HttpAsyncClients.createDefault()) {
client.start();
final CompletableFuture<HttpResponse> cf = new CompletableFuture<>();
client.execute(
HttpAsyncMethods.createGet("https://example.com"),
HttpAsyncMethods.createZeroCopyConsumer(new File("foo.html")),
new FutureCallback<HttpResponse>() {
@Override
public void completed(HttpResponse result) {
cf.complete(result);
}
@Override
public void failed(Exception ex) {
cf.completeExceptionally(ex);
}
@Override
public void cancelled() {
cf.cancel(true);
}
});
// When cf completes, the file will be ready.
// The InputStream inside the HttpResponse will be the FileInputStream of the created file.
}
来源:https://stackoverflow.com/questions/50294297/async-response-streaming-with-apache-async-http-client