Glide + okHttp3 去除图片流前八个字节后显示图片
作者:互联网
今天在学习群里碰到了一个问题:用Glide + okHttp3加载图片,这个时候有一个需求就是,有一个url对应的图片流,这个图片数据流需要去除前面八个字节后才能正常显示图片,所以那位大佬的思路就是添加一个okHttp的应用层拦截器,并在该拦截器中对图片流前面的八个字节进行移除,于是就有了最开始的下面这段代码
@GlideModule
public class MyOkHttpGlideModule extends AppGlideModule {
@Override
public void registerComponents(Context context, Glide glide, Registry registry) {
Log.i("zhang_xin", "减去了若干字节00000000");
//定制OkHttp
OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder();
//请求头设置
httpClientBuilder.interceptors().add(new ReduceByteInterceptor());
OkHttpClient okHttpClient = httpClientBuilder.build();
registry.replace(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory(okHttpClient));
}
public static class ReduceByteInterceptor implements Interceptor {
public ReduceByteInterceptor() {
}
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Response originalResponse = chain.proceed(request);
ResponseBody body = originalResponse.body();
// 获取
InputStream inputStream = body.byteStream();
inputStream.read();
Log.i("Test", "减去了若干字节前" + originalResponse.body().byteStream().getClass().getName());
inputStream.skip(7);
Log.i("Test", "减去了若干字节后" + originalResponse.body().byteStream().getClass().getName());
return originalResponse.newBuilder().body(body).build();
}
}
}
我们主要看一下ReduceByteInterceptor的intercept方法
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Response originalResponse = chain.proceed(request);
ResponseBody body = originalResponse.body();
InputStream inputStream = body.byteStream();
// 通过body.byteStream()获取的inputStream调用read和skip方法的时候都会操作body.source().buffer,具体原因看源码
inputStream.read();
inputStream.skip(7);
return originalResponse.newBuilder().body(body).build();
}
实际上
inputStream.read();
inputStream.skip(7);
等价于
inputStream.skip(8);
InputStream对象是通过ResponseBody.byteStream()获得的,调用该InputStream.read()方法的时候虽然会新建一个InputStream对象,但是无论哪个InputStream对象,操作的都是同一个ResponseBody.source().buffer,而ResponseBody.source().buffer就是网络实体数据的缓冲区。既然通过ResponseBody.byteStream()获取的InputStream对象操作的是ResponseBody.source().buffer,那我们可以直接操作buffer (直接操作buffer的话需要先调用InputStream.read()或者ResponseBody.source().readByte(),具体原因看源码,我是没怎么看懂,最好是不要这么使用) ,或者可以通过ResponseBody.source()操作buffer即可。
所以
ResponseBody body = originalResponse.body();
InputStream inputStream = body.byteStream();
inputStream.read();
inputStream.skip(7);
等价于
ResponseBody body = originalResponse.body();
// 这里便会直接操作buffer跳过8个字节
originalResponse.body().source().skip(8);
那这段代码运行得起来结果正确吗?
不正确!!
我们在Glide加一个载回调,打印出失败的信息
Glide.with(this)
.load("https://srcfiles-1301875640.cos.accelerate.myqcloud.com/movie/cn/yz/HKDOLL-014/HKDOLL-014.jpg")
.skipMemoryCache(true)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.addListener(new RequestListener<Drawable>() {
@Override
public boolean onl oadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
Log.i("Shzy", "onLoadFailed: " + e.getMessage());
return false;
}
@Override
public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
return false;
}
})
.into(imageView);
我们看看结果
这是java的Io异常,异常原因就是:预期收到296947个字节数据,但实际上只收到了296939个字节的数据,因为我们在拦截器移除了前面八个字节。而296947也就是预期收到的字节数实际上就是ResponseBody.contentLength(),也就是响应体的总长度(和响应头的Content-Length字段对应),而ResponseBody.contentLength属性是一个final对象我们没法直接修改,所以我们必须新建一个响应体,并保证contentLength和实际的内容长度一致
// 构建一个新的请求体,目的是让 content-Length = 去除8字节后的
ResponseBody newResponseBody = ResponseBody
.create(originalResponse.body().contentType(), bufferedSource.readByteArray());
return originalResponse.newBuilder().body(newResponseBody).build();
所以最后的代码应该是这样的
@GlideModule
public class MyOkHttpGlideModule extends AppGlideModule {
@Override
public void registerComponents(Context context, Glide glide, Registry registry) {
//定制OkHttp
OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder();
//请求头设置
httpClientBuilder.addInterceptor(new ReduceByteInterceptor());
OkHttpClient okHttpClient = httpClientBuilder.build();
registry.replace(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory(okHttpClient));
}
public static class ReduceByteInterceptor implements Interceptor {
public ReduceByteInterceptor() {
}
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Response originalResponse = chain.proceed(request);
if(originalResponse.body() == null){
return null;
}
BufferedSource bufferedSource = originalResponse.body().source();
bufferedSource.skip(8);
// 构建一个新的请求体,目的是让 contentLength 与 去除8字节后的内容长度一致
ResponseBody newResponseBody = ResponseBody
.create(originalResponse.body().contentType(), bufferedSource.readByteArray());
return originalResponse.newBuilder().body(newResponseBody).build();
}
}
}
标签:body,Glide,流前,inputStream,ResponseBody,originalResponse,InputStream,public,图片 来源: https://www.cnblogs.com/shzy-android/p/15841594.html