问题
I am using Async Http Client to download lots of (possibly large) files from the internet.
In my particular case, I need to send along the InputStream of bytes from these downloading URLs to another service to parse.
A naive approach would be to do this:
AsyncHttpClient asyncHttpClient = Dsl.asyncHttpClient(Dsl.config()
.setMaxConnectionsPerHost(-1)
.setMaxConnections(-1)
.setPooledConnectionIdleTimeout(60 * 10 * 1000)
.setConnectionTtl(6 * 60 * 1000)
.setConnectTimeout(5 * 1000)
.setRequestTimeout(5 * 60 * 1000)
.setFollowRedirect(true)
.setRealm(new Realm.Builder(username, password)
.setNtlmDomain(domain)
.setScheme(Realm.AuthScheme.NTLM)
.build())
Response httpGetResponse = asyncHttpClient.prepareGet(url).execute().get();
return httpGetResponse.getResponseBodyAsStream();
But in this tutorial for async http requests we learn that unlike HTTP Components http client, async http client will download the entire file to the memory.
This will, in my case, quickly cause OOMs.
So the alternative is this:
Response httpGetResponse = asyncHttpClient.prepareGet(url).execute(new AsyncHandler<Response>() {
private final Response.ResponseBuilder builder = new Response.ResponseBuilder();
@Override
public State onBodyPartReceived(HttpResponseBodyPart bodyPart) throws Exception {
bodyPart.getBodyByteBuffer(); // Each chunk of bytes will be fed into this method.
// I need to write these bytes to the resuting input stream
// without streaming them all into memory.
return State.CONTINUE;
}
@Override
public State onHeadersReceived(HttpHeaders headers) throws Exception {
builder.accumulate(headers);
return State.CONTINUE;
}
@Override
public State onStatusReceived(HttpResponseStatus responseStatus) throws Exception {
builder.accumulate(responseStatus);
return State.CONTINUE;
}
@Override
public Response onCompleted() throws Exception {
return builder.build();
}
@Override
public void onThrowable(Throwable t) {
}
}).get();
What is the easiest, cleanest way to get these bytes as they come to an input stream?
I have two ideas:
1) Write the input to file, then stream the file or 2) Return a piped input stream right away and the bytes will be written to the piped input stream as they are received.
Does anyone have a working example they can share with this?
回答1:
I correctly assumed someone had already done this. In fact, after I did a search on "async http client" and "piped input stream" i found this in the project itself:
https://github.com/AsyncHttpClient/async-http-client/blob/master/client/src/main/java/org/asynchttpclient/handler/BodyDeferringAsyncHandler.java
usage:
PipedInputStream pipedInputStream = new PipedInputStream();
PipedOutputStream pipedOutputStream = new PipedOutputStream(pipedInputStream);
BodyDeferringAsyncHandler bodyDeferringAsyncHandler = new BodyDeferringAsyncHandler(pipedOutputStream);
Future<Response> futureResponse = asyncHttpClient.prepareGet(url).execute(bodyDeferringAsyncHandler);
Response response = bodyDeferringAsyncHandler.getResponse();
if (response.getStatusCode() == 200) {
return new BodyDeferringAsyncHandler.BodyDeferringInputStream(futureResponse,
bodyDeferringAsyncHandler,
pipedInputStream);
} else {
return null;
}
来源:https://stackoverflow.com/questions/50402514/how-to-accomplish-an-async-http-client-input-stream-that-isnt-byte-array-input