Okio 3.12.0在KMP中实现Deflate压缩解压缩时的异常问题排查
Okio 3.12.0在KMP中实现Deflate压缩解压缩时的异常问题排查
我来帮你定位这个问题——你的Okio压缩函数里漏掉了关键的压缩收尾操作,导致压缩数据根本没被真正写入输出Buffer里,这就是为什么输出size为0,后续解压会抛出EOF异常的原因。
问题根源分析
Java的DeflaterOutputStream在use块结束时会自动调用close()方法,这个方法会触发三个核心操作:
- 通知Deflater完成所有剩余数据的压缩(调用
def.finish()) - 将Deflater缓冲中所有压缩后的数据写入输出流
- 关闭流资源
但Okio的DeflaterSink不会自动做这些收尾工作。你的原代码只是把数据写入了Deflater的输入缓冲,但Deflater还在等待更多输入,没有触发最终的压缩和数据刷出,所以输出Buffer始终是空的。
修复后的Okio压缩函数
只需要用use块包裹DeflaterSink(或者手动关闭它),就能确保压缩流程完整执行:
fun encode(sb : ByteArray): ByteArray { val source = Buffer() source.write(sb) val out = Buffer() // 使用use块自动关闭DeflaterSink,触发压缩收尾逻辑 out.deflate().use { deflatorSink -> deflatorSink.write(source, source.size) } println(" org size = ${sb.size}") println("encoded size = ${out.size}") return out.readByteArray() }
如果需要和Java版本一样指定压缩级别(比如level 5),可以显式创建指定级别的Deflater:
fun encode(sb : ByteArray): ByteArray { val source = Buffer() source.write(sb) val out = Buffer() val compressionLevel = 5 out.deflate(deflater = Deflater(compressionLevel)).use { deflatorSink -> deflatorSink.write(source, source.size) } println(" org size = ${sb.size}") println("encoded size = ${out.size}") return out.readByteArray() }
手动关闭的等价写法
如果不想用use块,也可以手动在finally块中关闭DeflaterSink,效果是一样的:
fun encode(sb : ByteArray): ByteArray { val source = Buffer() source.write(sb) val out = Buffer() val deflatorSink = out.deflate() try { deflatorSink.write(source, source.size) } finally { // 关键:关闭触发压缩收尾和数据刷出 deflatorSink.close() } println(" org size = ${sb.size}") println("encoded size = ${out.size}") return out.readByteArray() }
为什么你的JVM版本能工作?
Java的DeflaterOutputStream在use块结束时的自动关闭逻辑,替你完成了所有收尾工作。而Okio作为更底层的IO库,把这些控制权交给了开发者,需要显式触发收尾操作。
修复后,你的KMP测试代码应该就能和JVM版本一样正常运行,压缩和解压后的大小也能匹配预期了。
内容来源于stack exchange
相关产品推荐
相关产品推荐

