改造:如何在没有内容编码的情况下解析 GZIP 响应:gzip 标头

问题描述 投票:0回答:3

我正在尝试处理经过 GZIP 处理的服务器响应。响应带有标题

Content-Type: application/x-gzip

但没有标题

Content-Encoding: gzip

如果我使用代理添加该标头,响应就会被很好地解析。 我对服务器没有任何控制权,因此无法添加标头。

我可以强制 Retrofit 将其视为 GZIP 内容吗?有没有更好的办法? 服务器的 URL 是: http://crowdtorch.cms.s3.amazonaws.com/4474/Updates/update-1.xml

gzip retrofit2 okhttp
3个回答
12
投票

我想通了。这个想法是添加一个自定义拦截器,它将获取尚未解压缩的响应,并“手动”解压缩它 - 执行与 OkHttp 基于 Content-Encoding 标头自动执行的操作相同的操作,但不需要该标头。

就像这样:

    OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder()
            .addInterceptor(new UnzippingInterceptor());
    OkHttpClient client = clientBuilder.build();

拦截器就像这样:

private class UnzippingInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Response response = chain.proceed(chain.request());
        return unzip(response);
    }
}

解压功能就像dis:

    // copied from okhttp3.internal.http.HttpEngine (because is private)
private Response unzip(final Response response) throws IOException {

    if (response.body() == null) {
        return response;
    }

    GzipSource responseBody = new GzipSource(response.body().source());
    Headers strippedHeaders = response.headers().newBuilder()
            .removeAll("Content-Encoding")
            .removeAll("Content-Length")
            .build();
    return response.newBuilder()
            .headers(strippedHeaders)
            .body(new RealResponseBody(strippedHeaders, Okio.buffer(responseBody)))
            .build();
}

4
投票

有比重新发明轮子更好的方法。只需自己添加

Content-Encoding
标题即可。

.addNetworkInterceptor((Interceptor.Chain chain) -> {
    Request req = chain.request();
    Headers.Builder headersBuilder = req.headers().newBuilder();

    String credential = Credentials.basic(...);
    headersBuilder.set("Authorization", credential);

    Response res = chain.proceed(req.newBuilder().headers(headersBuilder.build()).build());

    return res.newBuilder()
        .header("Content-Encoding", "gzip")
        .header("Content-Type", ""application/json")
        .build();
})

事实上,您的代码是使用内部代码(如 JDK 中的

com.sun
包)的弊端的典型示例。
RealResponseBody
不再有那个构造函数了。


0
投票

这对我的 gzip 文件有用:

private fun decompressGzip(responseBody: ResponseBody): String {
    try {
        // Create a GZIPInputStream to decompress the data
        val gzipInputStream = GZIPInputStream(responseBody.byteStream())

        // Use a ByteArrayOutputStream to hold the decompressed data
        val byteArrayOutputStream = ByteArrayOutputStream()

        // Buffer for reading data
        val buffer = ByteArray(1024)
        var bytesRead: Int

        // Read from the GZIPInputStream and write to the ByteArrayOutputStream
        while (gzipInputStream.read(buffer).also { bytesRead = it } != -1) {
            byteArrayOutputStream.write(buffer, 0, bytesRead)
        }

        // Convert the ByteArrayOutputStream to a byte array
        val decompressedData = byteArrayOutputStream.toByteArray()

        // Optionally convert the byte array to a string or other format as needed
        return String(decompressedData, StandardCharsets.UTF_8)

    } catch (e: Exception) {
        e.printStackTrace()
        // Handle exceptions
    }

    return ""
}

fun getGzip(
    country: String,
    onRadioMutableListGetCallback: OnRadioListGetCallback
) {
    val call = apiServiceBackup.MutableListByCountryBackup(
        country.toLowerCase(),
    )
    call.enqueue(object : Callback<ResponseBody> {
        override fun onResponse(call: Call<ResponseBody>, response: Response<ResponseBody>) {
            val res = response.body() ?: return
            val json = decompressGzip(res)
            val gson = Gson()
            val myObjectListType = object : TypeToken<List<RadioData>>() {}.type
            val stations = gson.fromJson<MutableList<RadioData>>(json, myObjectListType)
            onRadioMutableListGetCallback.onRadioListGet(stations)
        }

        override fun onFailure(call: Call<ResponseBody>, t: Throwable) {
            onRadioMutableListGetCallback.onRadioListGet(ArrayList())
        }
    })
}

    @GET("{country}.json.gz")
    fun MutableListByCountryBackup(
        @Path("country") user: String?
    ): Call<ResponseBody>
© www.soinside.com 2019 - 2024. All rights reserved.