ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Glide + okHttp3 去除图片流前八个字节后显示图片

2022-01-25 03:00:11  阅读:187  来源: 互联网

标签:body Glide 流前 inputStream ResponseBody originalResponse InputStream public 图片


今天在学习群里碰到了一个问题:用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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有