Java 记录OkHttp响应正文大小而不缓冲整个内容

Java 记录OkHttp响应正文大小而不缓冲整个内容,java,okhttp3,Java,Okhttp3,我希望能够在使用OkHttp获取内容时记录web服务器响应体的实际大小(用于监视目的) 是否有一种方法可以获得响应正文的实际大小(注意-不是服务器声称的内容长度),而不需要将整个响应缓冲到内存中?这似乎符合我的要求 AtomicLong bytesRead = new AtomicLong(); OkHttpClient client = new OkHttpClient.Builder().addNetworkInterceptor(new Interceptor() { @Ove

我希望能够在使用OkHttp获取内容时记录web服务器响应体的实际大小(用于监视目的)


是否有一种方法可以获得响应正文的实际大小(注意-不是服务器声称的内容长度),而不需要将整个响应缓冲到内存中?

这似乎符合我的要求

AtomicLong bytesRead = new AtomicLong();

OkHttpClient client = new OkHttpClient.Builder().addNetworkInterceptor(new Interceptor() {

    @Override
    public Response intercept(Chain chain) throws IOException {
        Response response = chain.proceed(chain.request());

        response = response.newBuilder()
            .body(
                new RealResponseBody(
                    response.body().contentType().toString(),
                    response.body().contentLength(),
                    Okio.buffer(
                        new LengthTrackingSource(
                            response.body().source(),
                            (newBytesRead) -> {
                                bytesRead.addAndGet(newBytesRead);
                            }
                        )
                    )
                )
            )
            .build();

        return response;
    }

    class LengthTrackingSource implements Source {

        private Source source; 


        private Consumer<Long> lengthRecorder;

        public LengthTrackingSource(Source source, Consumer<Long> lengthRecorder) {
            this.source = source;
            this.lengthRecorder = lengthRecorder;
        }

        @Override
        public long read(Buffer sink, long byteCount) throws IOException {
            long bytesRead;
            try {
                bytesRead = source.read(sink, byteCount);
            } catch (IOException e) {
                throw e;
            }

            // Avoid adding the final -1 (which means the source is exhausted)
            if (bytesRead > 0) {
                lengthRecorder.accept(bytesRead);
            }

            return bytesRead;
        }

        @Override
        public Timeout timeout() {
            return source.timeout();
        }

        @Override
        public void close() throws IOException {
            source.close();
        }
    }

}).build();

try (Response response = client.newCall(new Request.Builder().url("http://example.com/").build()).execute()) {
    System.out.println(bytesRead.get());

    String body = response.body().string();
    System.out.println(body.length());

    System.out.println(bytesRead.get());
}
AtomicLong bytesRead=new AtomicLong();
OkHttpClient客户端=新建OkHttpClient.Builder().addNetworkInterceptor(新建Interceptor()){
@凌驾
公共响应拦截(链)引发IOException{
响应=chain.procedure(chain.request());
response=response.newBuilder()
.身体(
新RealResponseBody(
response.body().contentType().toString(),
response.body().contentLength(),
奥基奥缓冲区(
新长度跟踪源(
response.body().source(),
(新字节READ)->{
bytesRead.addAndGet(新字节读取);
}
)
)
)
)
.build();
返回响应;
}
类LengthTrackingSource实现源{
私人来源;
私人用户长度记录器;
公共长度跟踪源(源源、用户长度记录器){
this.source=源;
this.lengthRecorder=长度记录器;
}
@凌驾
公共长读(缓冲区接收器、长字节数)引发IOException{
漫长的过去;
试一试{
bytesRead=source.read(sink,byteCount);
}捕获(IOE异常){
投掷e;
}
//避免添加final-1(这意味着源已耗尽)
如果(字节读取>0){
长度记录器。接受(字节读取);
}
返回字节读取;
}
@凌驾
公共超时(){
返回source.timeout();
}
@凌驾
public void close()引发IOException{
source.close();
}
}
}).build();
try(Response-Response=client.newCall(newrequest.Builder().url)()http://example.com/).build()).execute()){
System.out.println(bytesRead.get());
String body=response.body().String();
System.out.println(body.length());
System.out.println(bytesRead.get());
}

如果有更简单的方法,我还是很想听听

但是服务器的contentLength是正确的,对吗?查看此文档:服务器可能根本不提供内容长度,因为内容是从不确定长度的某个位置流式传输的(即ResponseBody.contentLength返回-1的情况)。。。但是,它也不一定是我的服务器,而且它可能确实返回了不正确的长度头(虽然okhttp可能已经不允许这样做了)。啊,还没有正确地消化它,但是看起来ReadTworth的东西可能是一种方法(第二次读取的结果只是计算它所看到的数据的长度,而忽略了实际内容)。您是否考虑过使用新的EventListener API?感谢此解决方案!似乎大多数答案都只是从服务器上查看内容长度,或者读取两次响应。