OkHttp做下载库的一个问题

1,619 阅读2分钟

在说问题前,先确认一个http响应头的一个字段Content-Length的解释。

对于http的请求返回结果要进行内容的长度校验主要有两种方式,二者互斥使用

  1. 客户端在http头(head)加Connection:keep-alive时,服务器的response是Transfer-Encoding:chunked的形式,通知页面数据是否接收完毕,例如长连接或者程序运行中可以动态的输出内容,例如一些运算比较复杂且需要用户及时的得到最新结果,那就采用chunked编码将内容分块输出。
  2. 除了如1所述之外的情况一般都是可以获取到Content-Length的。在HTTP协议中,Content-Length用于描述HTTP消息实体的传输长度the transfer-length of the message-body。在HTTP协议中,消息实体长度和消息实体的传输长度是有区别,比如说gzip压缩下,消息实体长度是压缩前的长度,消息实体的传输长度是gzip压缩后的长度。

OkHttp处理Gzip流

一般下载过程中需要用到Content-Length来做断点续传以及长度校验。但是OkHttp在用户请求中未带Content-Encoding会默认加上gzip的方式,同时在返回流解压完后,会剔除掉这个字段,导致Content-Length获取不到,同时因为后面取到的流已经是解压后的流了,所以这个其实也没啥意义了。

可以参考下BridgeInterceptor的源码

// If we add an "Accept-Encoding: gzip" header field we're responsible for also decompressing
    // the transfer stream.
    var transparentGzip = false
    if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
      transparentGzip = true
      requestBuilder.header("Accept-Encoding", "gzip")
    }
val networkResponse = chain.proceed(requestBuilder.build())

    HttpHeaders.receiveH
eaders(cookieJar, userRequest.url(), networkResponse.headers())

    val responseBuilder = networkResponse.newBuilder()
        .request(userRequest)

    if (transparentGzip &&
        "gzip".equals(networkResponse.header("Content-Encoding"), ignoreCase = true) &&
        HttpHeaders.hasBody(networkResponse)) {
      val responseBody = networkResponse.body()
      if (responseBody != null) {
        val gzipSource = GzipSource(responseBody.source())
        val strippedHeaders = networkResponse.headers().newBuilder()
            .removeAll("Content-Encoding")
            .removeAll("Content-Length")
            .build()
        responseBuilder.headers(strippedHeaders)
        val contentType = networkResponse.header("Content-Type")
        responseBuilder.body(RealResponseBody(contentType, -1L, gzipSource.buffer()))
      }
    }

    return responseBuilder.build()
  }

不得不说okhttp在此处的处理略显粗暴,但也不是完全不能理解。

所以如果想使用OkHttp作为下载框架,必须只能自己对请求添加Content-Encoding,然后自己处理Gzip流。

参考文章

  1. okhttp——BridgeInterceptor
  2. http 响应头里面Content-Length字段详解