最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

解決OkHttp接收gzip壓縮數(shù)據(jù)返回亂碼問題

 更新時(shí)間:2022年06月17日 15:20:44   作者:云飛揚(yáng)1  
這篇文章主要介紹了解決OkHttp接收gzip壓縮數(shù)據(jù)返回亂碼問題,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

問題

Retrofit 是現(xiàn)在最流行的網(wǎng)絡(luò)開發(fā)框架之一,功能十分強(qiáng)大,但是最近確遇到一個(gè)十分坑的問題,現(xiàn)在記錄下來,希望看到的人能注意下。

眾所周知,在 HTTP 傳輸時(shí)是支持 gzip 壓縮的,客戶端發(fā)起請(qǐng)求時(shí)在請(qǐng)求頭里增加 Accept-Encoding: gzip,服務(wù)端響應(yīng)時(shí)在返回的頭信息里增加 Content-Encoding: gzip,這表示傳輸?shù)臄?shù)據(jù)是采用 gzip 壓縮的。默認(rèn)情況下,傳輸內(nèi)容是不壓縮的,采用 gzip 壓縮后可以大幅減少傳輸內(nèi)容大小,這樣可以提高傳輸速度,減少流量的使用。

請(qǐng)求頭信息

本來 OkHttp 是默認(rèn)支持 gzip 解壓縮的,不需要額外配置的。但是我在攔截器里統(tǒng)一添加了很多請(qǐng)求頭信息,大概代碼如下:

public class RequestInterceptor implements Interceptor {
    public RequestInterceptor() {
    }
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request.Builder builder = chain.request()
                .newBuilder()
                .addHeader("Accept", "application/json")
                .addHeader("Accept-Encoding", "gzip");
        Request request = builder.build();         
        return chain.proceed(request);
    }
}

以前服務(wù)端沒有開啟 gzip 壓縮,一直都沒有問題,某天突然運(yùn)維加了 gzip 壓縮,說是為了要省流量帶寬,結(jié)果就悲劇了,我們 Android APP 里所有的接口都報(bào)錯(cuò)了,明明前一秒都是OK的,后一秒就都不能訪問了,但是 iOS 里卻能正常訪問,這是最令人崩潰的事情。

立即進(jìn)行代碼調(diào)試,發(fā)現(xiàn) Android 里的 http 請(qǐng)求返回的都是亂碼字符串了,其實(shí)這些都是 gzip 壓縮的數(shù)據(jù),不是說 OkHttp 是自動(dòng)支持 gzip 解壓縮的嗎?為什么我們的返回?cái)?shù)據(jù)沒有進(jìn)行 gzip 解壓?還有一個(gè)奇怪的現(xiàn)象是,當(dāng)我把這段代碼 addHeader("Accept-Encoding", "gzip") 去掉之后,一切又恢復(fù)正常了。

BridgeInterceptor攔截器

這是一個(gè)很費(fèi)解的問題,當(dāng)我手動(dòng)加上這個(gè)頭信息時(shí),OkHttp 不會(huì)自動(dòng)解壓 gzip 流,當(dāng)我去掉時(shí) OkHttp 又會(huì)自動(dòng)解壓 gzip 流了,秉著刨根究底的精神我翻看了源碼,終于找到了原因。原來 OkHttp 在最終構(gòu)建請(qǐng)求信息以及處理返回信息時(shí),內(nèi)部使用了一個(gè)叫做 BridgeInterceptor 的攔截器,該類的代碼如下:

public final class BridgeInterceptor implements Interceptor {
  private final CookieJar cookieJar;
  public BridgeInterceptor(CookieJar cookieJar) {
    this.cookieJar = cookieJar;
  }
  @Override public Response intercept(Chain chain) throws IOException {
    Request userRequest = chain.request();
    Request.Builder requestBuilder = userRequest.newBuilder();
    RequestBody body = userRequest.body();
    if (body != null) {
      MediaType contentType = body.contentType();
      //自動(dòng)增添加請(qǐng)求頭 Content-Type
      if (contentType != null) {
        requestBuilder.header("Content-Type", contentType.toString());
      }
      long contentLength = body.contentLength();
      //如果傳輸長(zhǎng)度不為-1,則表示完整傳輸
      if (contentLength != -1) {
        //設(shè)置頭信息 Content-Length
        requestBuilder.header("Content-Length", Long.toString(contentLength));
        requestBuilder.removeHeader("Transfer-Encoding");
      } else {
        //如果傳輸長(zhǎng)度為-1,則表示分塊傳輸,自動(dòng)設(shè)置頭信息         
        requestBuilder.header("Transfer-Encoding", "chunked");
        requestBuilder.removeHeader("Content-Length");
      }
    }
    if (userRequest.header("Host") == null) {
      requestBuilder.header("Host", hostHeader(userRequest.url(), false));
    }
    //如果沒有設(shè)置頭信息 Connection,則自動(dòng)設(shè)置為 Keep-Alive
    if (userRequest.header("Connection") == null) {
      requestBuilder.header("Connection", "Keep-Alive");
    }
    // If we add an "Accept-Encoding: gzip" header field we're responsible for also decompressing
    // the transfer stream.
    boolean transparentGzip = false;
    if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
      //如果我們沒有在請(qǐng)求頭信息里增加Accept-Encoding,在這里會(huì)自動(dòng)設(shè)置頭信息 Accept-Encoding = gzip
      transparentGzip = true;
      requestBuilder.header("Accept-Encoding", "gzip");
    }
    List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url());
    if (!cookies.isEmpty()) {
      requestBuilder.header("Cookie", cookieHeader(cookies));
    }
    if (userRequest.header("User-Agent") == null) {
      requestBuilder.header("User-Agent", Version.userAgent());
    }
    Response networkResponse = chain.proceed(requestBuilder.build());
    HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());
    Response.Builder responseBuilder = networkResponse.newBuilder()
        .request(userRequest);
    //如果返回的頭信息里Content-Encoding = gzip,并且我們沒有手動(dòng)在請(qǐng)求頭信息里設(shè)置 Accept-Encoding = gzip,則會(huì)進(jìn)行 gzip 解壓數(shù)據(jù)流
    if (transparentGzip
        && "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding"))
        && HttpHeaders.hasBody(networkResponse)) {
      GzipSource responseBody = new GzipSource(networkResponse.body().source());
      Headers strippedHeaders = networkResponse.headers().newBuilder()
          .removeAll("Content-Encoding")
          .removeAll("Content-Length")
          .build();
      responseBuilder.headers(strippedHeaders);
      String contentType = networkResponse.header("Content-Type");
      responseBuilder.body(new RealResponseBody(contentType, -1L, Okio.buffer(responseBody)));
    }
    return responseBuilder.build();
  }
}

上面代碼關(guān)鍵地方我做了注釋,OkHttp會(huì)額外的增加很多請(qǐng)求頭信息,如果我們?cè)诖a里沒有手動(dòng)設(shè)置Accept-Encoding = gzip,那么OkHttp會(huì)自動(dòng)處理gzip的解壓縮;反之,你需要手動(dòng)對(duì)返回的數(shù)據(jù)流進(jìn)行g(shù)zip解壓縮。

以上就是我的代碼里 gzip 處理失敗的根本原因了,更多關(guān)于OkHttp接收gzip壓縮數(shù)據(jù)返回亂碼的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java使用Knife4j優(yōu)化Swagger接口文檔的操作步驟

    Java使用Knife4j優(yōu)化Swagger接口文檔的操作步驟

    在現(xiàn)代微服務(wù)開發(fā)中,接口文檔的質(zhì)量直接影響了前后端協(xié)作效率,Swagger 作為一個(gè)主流的接口文檔工具,雖然功能強(qiáng)大,但其默認(rèn)界面和部分功能在實(shí)際使用中略顯不足,而 Knife4j 的出現(xiàn)為我們提供了一種增強(qiáng)的選擇,本篇文章將詳細(xì)介紹如何在項(xiàng)目中集成和使用 Knife4j
    2024-12-12
  • Spring在代碼中獲取bean的方法小結(jié)

    Spring在代碼中獲取bean的方法小結(jié)

    在工作中有時(shí)候我們需要在非spring依賴注入或管理的類中獲取service、dao等bean對(duì)象,這時(shí)候用@Autowired和@Resource顯然是不行的,那么下面這篇文章就給大家了整理幾種獲取bean的方式,對(duì)大家的學(xué)習(xí)和工作具有一定的參考借鑒,下面來一起看看吧。
    2016-11-11
  • mybatis單筆批量保存實(shí)體數(shù)據(jù)的方法

    mybatis單筆批量保存實(shí)體數(shù)據(jù)的方法

    這篇文章主要介紹了mybatis單筆批量保存實(shí)體數(shù)據(jù)的相關(guān)知識(shí),非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2018-01-01
  • Java生成UUID的常用方式示例代碼

    Java生成UUID的常用方式示例代碼

    UUID保證對(duì)在同一時(shí)空中的所有機(jī)器都是唯一的,通常平臺(tái)會(huì)提供生成的API,按照開放軟件基金會(huì)(OSF)制定的標(biāo)準(zhǔn)計(jì)算,用到了以太網(wǎng)卡地址、納秒級(jí)時(shí)間、芯片ID碼和許多可能的數(shù)字,下面這篇文章主要給大家介紹了關(guān)于Java生成UUID的常用方式,需要的朋友可以參考下
    2023-05-05
  • 一文帶你熟練掌握J(rèn)ava中的日期時(shí)間相關(guān)類

    一文帶你熟練掌握J(rèn)ava中的日期時(shí)間相關(guān)類

    我們?cè)陂_發(fā)時(shí),除了數(shù)字、數(shù)學(xué)這樣的常用API之外,還有日期時(shí)間類,更是會(huì)被經(jīng)常使用,比如我們項(xiàng)目中必備的日志功能,需要記錄異常等信息產(chǎn)生的時(shí)間,本文就帶各位來學(xué)習(xí)一下相關(guān)的日期時(shí)間類有哪些
    2023-05-05
  • 利用SpringBoot和LiteFlow解鎖復(fù)雜流程

    利用SpringBoot和LiteFlow解鎖復(fù)雜流程

    隨著業(yè)務(wù)的復(fù)雜化,企業(yè)需要更加高效、便捷地管理自己的業(yè)務(wù)流程,這就需要借助一些流程引擎實(shí)現(xiàn),今天,我們就來介紹一種基于Java語言開發(fā)的輕量級(jí)工作流引擎——LiteFlow,以及如何在Spring Boot框架中集成它,從而提高企業(yè)的工作效率和開發(fā)效率
    2023-06-06
  • MyBatis-Plus?分頁查詢的實(shí)現(xiàn)示例

    MyBatis-Plus?分頁查詢的實(shí)現(xiàn)示例

    本文主要介紹了MyBatis-Plus?分頁查詢的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • Springboot實(shí)現(xiàn)人臉識(shí)別與WebSocket長(zhǎng)連接的實(shí)現(xiàn)代碼

    Springboot實(shí)現(xiàn)人臉識(shí)別與WebSocket長(zhǎng)連接的實(shí)現(xiàn)代碼

    這篇文章主要介紹了Springboot實(shí)現(xiàn)人臉識(shí)別與WebSocket長(zhǎng)連接的實(shí)現(xiàn),本文通過示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2023-11-11
  • 詳解JAVA后端實(shí)現(xiàn)統(tǒng)一掃碼支付:微信篇

    詳解JAVA后端實(shí)現(xiàn)統(tǒng)一掃碼支付:微信篇

    本篇文章主要介紹了詳解JAVA后端實(shí)現(xiàn)統(tǒng)一掃碼支付:微信篇,這里整理了詳細(xì)的代碼,有需要的小伙伴可以參考下。
    2017-01-01
  • 基于Java實(shí)現(xiàn)簡(jiǎn)單貪吃蛇游戲

    基于Java實(shí)現(xiàn)簡(jiǎn)單貪吃蛇游戲

    這篇文章主要為大家詳細(xì)介紹了基于Java實(shí)現(xiàn)簡(jiǎn)單貪吃蛇,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-04-04

最新評(píng)論

白玉县| 平乐县| 大庆市| 寿宁县| 梧州市| 封丘县| 山东省| 临澧县| 苏州市| 康保县| 夏河县| 伽师县| 澎湖县| 广汉市| 全椒县| 阿克陶县| 灯塔市| 仪征市| 海晏县| 通江县| 绥棱县| 洛浦县| 湖口县| 孟村| 平江县| 抚宁县| 龙陵县| 长丰县| 巴中市| 怀远县| 合江县| 阜康市| 海阳市| 广平县| 瑞昌市| 和平县| 正定县| 咸丰县| 兴义市| 耿马| 扎鲁特旗|