retrofit + okhttp : Retrieve GZIPInputStream

后端 未结 4 1237
自闭症患者
自闭症患者 2021-02-19 19:33

I have a problem when i activate gzip on WS using retrofit 1.4.1 and okhttp 1.3.0.

RequestInterceptor requestInterceptor = new RequestInterceptor() {
                    


        
4条回答
  •  旧巷少年郎
    2021-02-19 19:56

    After running into a similar issue (in my case, without adding any Accept-Encoding header, it would occasionally fail to un-gzip the response, leaving also the Content-Encoding: gzip header in it, crashing the JSON parser), and with no clear way around this, I manually enabled gzip for Retrofit by creating the delegated Client implementation below. It works great, except that you probably should not use it for very large (e.g. > 250KB) responses, as they are first copied into a byte array.

    public class GzippedClient implements Client {
    
        private Client wrappedClient;
    
        public GzippedClient(Client wrappedClient) {
            this.wrappedClient = wrappedClient;
        }
    
        @Override
        public Response execute(Request request) throws IOException {
            Response response = wrappedClient.execute(request);
    
            boolean gzipped = false;
            for (Header h : response.getHeaders()) {
                if (h.getName() != null && h.getName().toLowerCase().equals("content-encoding") && h.getValue() != null && h.getValue().toLowerCase().equals("gzip")) {
                    gzipped = true;
                    break;
                }
            }
    
            Response r = null;
            if (gzipped) {
                InputStream is = null;
                ByteArrayOutputStream bos = null;
    
                try {
                    is = new BufferedInputStream(new GZIPInputStream(response.getBody().in()));
                    bos = new ByteArrayOutputStream();
    
                    int b;
                    while ((b = is.read()) != -1) {
                        bos.write(b);
                    }
    
                    TypedByteArray body = new TypedByteArray(response.getBody().mimeType(), bos.toByteArray());
                    r = new Response(response.getUrl(), response.getStatus(), response.getReason(), response.getHeaders(), body);
                } finally {
                    if (is != null) {
                        is.close();
                    }
                    if (bos != null) {
                        bos.close();
                    }
                }
            } else {
                r = response;
            }
            return r;
        }
    
    }
    

    You will also have to add an Accept-Encoding header to your requests, e.g. by using a RequestInterceptor

    requestFacade.addHeader("Accept-Encoding", "gzip");
    

    Finally, you have to wrap your existing Client into this new GzippedClient, like so:

    restBuilder.setClient(new GzippedClient(new OkClient(okHttpClient)));
    

    That's it. Now your data will be gzipped.

    EDIT: It seems that in OkHttp version 1.5.1, a bug (https://github.com/square/okhttp/pull/632) seems to have been fixed related to the transparent gzipping which may (or may not) have been the source of my initial issue. If so, the occasional failure to un-gzip may no longer occur, though it happened rarely enough that I cannot confirm this yet. Either way, if you want to rely on your own, rather than the transparent adding/removing of headers and gzipping, then the solution described will work.

提交回复
热议问题