You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Ktor Client处理application/x-gzip类型压缩XML文件的解压缩配置问题

Ktor Client处理application/x-gzip类型压缩XML文件的解压缩配置问题

嘿,我看你在纠结Ktor Client处理application/x-gzip类型的压缩XML文件时,怎么让它自动解压缩并解析的问题,刚好我之前也碰到过类似的情况,来给你梳理下解决方案!

首先得搞清楚问题出在哪:你配置的ContentEncoding插件,其实是用来处理**响应头带Content-Encoding: gzip**的传输编码场景,但这次服务器返回的是Content-Type: application/x-gzip——意思是整个响应体本身就是一个gzip压缩包,不是普通XML响应加个压缩头,所以ContentEncoding插件不会自动触发解压缩操作。

那该怎么调整呢?我们可以在XML解析的前置步骤里,手动先把gzip内容解压缩,再交给XML序列化器处理。下面是具体的修改方案:

1. 自定义能处理gzip解压缩的XML转换器

我们可以扩展ContentNegotiation的xmlGzip配置,让它先解压缩字节流再解析XML:

import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.serialization.kotlinx.xml.*
import kotlinx.serialization.xml.*
import io.ktor.http.*
import io.ktor.util.io.*
import java.util.zip.GZIPInputStream

fun Configuration.xmlGzip(
    format: XML = DefaultXml,
    contentType: ContentType = ContentType("application", "x-gzip")
) {
    // 先拿到默认的XML转换器
    val xmlConverter = KotlinxSerializationXmlConverter(format)
    // 给application/x-gzip类型注册自定义序列化逻辑
    serialization(contentType) {
        // 定义解压缩逻辑:把gzip字节流转为普通字节流
        suspend fun transformInput(charset: Charset, input: ByteReadChannel): ByteReadChannel {
            val compressedBytes = input.readRemaining().readBytes()
            val decompressedStream = GZIPInputStream(compressedBytes.inputStream())
            return ByteReadChannel(decompressedStream.readBytes())
        }

        // 反序列化时先解压缩,再用默认XML转换器解析
        deserialize { charset, input ->
            val decompressedInput = transformInput(charset, input)
            xmlConverter.deserialize(charset, decompressedInput)
        }

        // 序列化逻辑沿用默认的XML即可(如果不需要发送gzip压缩的XML,不用改)
        serialize { contentType, value ->
            xmlConverter.serialize(contentType, value)
        }
    }
}

2. 调整HttpClient的整体配置

接下来修改你的客户端配置,保留必要的插件同时,确保兼容普通XML和gzip压缩的XML响应:

fun getHttpXmlClient(timeout: Long = 1500): HttpClient = HttpClient {
    install(HttpTimeout) {
        requestTimeoutMillis = timeout
        connectTimeoutMillis = 1500
        socketTimeoutMillis = 900
    }
    // 保留ContentEncoding,处理带Content-Encoding头的普通压缩场景
    install(ContentEncoding) {
        gzip()
        deflate()
        identity()
    }
    install(ContentNegotiation) {
        // 注册我们自定义的gzip+XML转换器
        xmlGzip()
        // 同时保留普通XML转换器,兼容非压缩的XML响应
        xml(DefaultXml)
    }
    install(DefaultRequest) {
        // 调整Accept头,明确告诉服务器我们接受application/x-gzip类型
        headers.append("Accept", "application/xml, application/x-gzip, */*;q=0.8")
        headers.append("Accept-Encoding", "gzip, deflate")
    }
}

额外的备选方案(手动处理)

如果你不想折腾自定义转换器,也可以手动接收字节流再解压缩解析,虽然不够优雅但胜在直观:

val client = getHttpXmlClient()
val response = client.get("你的目标URL")
// 先拿到压缩的字节数组
val compressedBytes = response.body<ByteArray>()
// 手动解压缩
val decompressedBytes = GZIPInputStream(compressedBytes.inputStream()).readBytes()
// 转成字符串后用XML解析器处理
val xmlContent = String(decompressedBytes, Charsets.UTF_8)
// 后续就是你原本的XML解析逻辑

简单总结下核心逻辑:application/x-gzip是实体本身为压缩包,不是传输层的压缩编码,所以不能靠ContentEncoding插件自动处理,必须在XML解析前手动介入解压缩步骤,把压缩的字节流转成普通XML字节流再交给序列化器。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.07 07:49:34