解压 zlib 数据流,困扰了一天多了没能解决

2024-02-21 15:01:21 +08:00
 rekulas

对接 discord 的 zlib 数据流,它是分段传输的,只有第一个片段包含头部信息,后续片段不包含头部信息。解压后面的片段需要依赖第一个段的 header
python 里很好实现

deobj = zlib.decompressobj()

然后只要用 deobj 按顺序解压每个片段就行了
deobj.decompress(数据流)

改写到 go 里,发现 zlib.NewReader()只能用于第一个片段,后续片段无法解压(会报 invalid header 错误) 感觉 go 的 zlib 没有提供一个能保存上下文状态的对象,每次解压都要从头开始,导致后续不完整的 zlib 流无法解压

reader := bytes.NewReader(bin1)
zlib.NewReader(reader)

reader = bytes.NewReader(bin2)
zlib.NewReader(reader) // 会报错

binmerge := append(bin1, bin2...)
reader = bytes.NewReader(binmerge)
zlib.NewReader(reader) // 正常

如果把 bin1 bin2 合并起来是可以解压的,但显然这样不合理,内存和资源占用会越来越大
所以我想只依赖 bin1 的 header 信息,然后用 bin2 的数据流来解压
除此之外还尝试过讲 bin1 的头 N 个字节放到 bin2 前面,也没成功,似乎 header 不是单纯拷贝字节那么简单,可能涉及到其他计算
问过 gpt ,没能解决。
搜过 google 等各种资料,也没有解决。
不熟悉 zlib 格式,想问问各位有没有办法实现

2 个对应的测试数据我打包放在这里了 https://drive.google.com/file/d/1zdAbgWVWewqcovaHxRq3ZhPObPnr3m-5/view?usp=sharing

2348 次点击
所在节点    Go 编程语言
24 条回复
rekulas
2024-03-16 08:26:45 +08:00
@lesismal 感谢大佬帮忙, 白天有点事 晚上回来测测
body007
2024-03-16 09:25:49 +08:00
测试没问题,就像你用 python 一样,只需要创建一个 zlib.NewReader ,使用 io.Pipe 就可以了。

```go

package main

import (
"compress/zlib"
"errors"
"io"
"os"
)

func main() {
err := test()
if err != nil {
panic(err)
}
}

func test() error {
var (
ir, iw = io.Pipe()
done = errors.New("done")
)

go func() {
list := []string{"1.bin", "2.bin"}
for _, f := range list {
fr, err := os.Open(f)
if err != nil {
iw.CloseWithError(err)
return
}

_, err = io.Copy(iw, fr)
if err1 := fr.Close(); err == nil {
err = err1
}

if err != nil {
iw.CloseWithError(err)
return
}
}
iw.CloseWithError(done)
}()

fw, err := os.Create("dst.txt")
if err != nil {
return err
}
defer fw.Close()

zr, err := zlib.NewReader(ir)
if err != nil {
return err
}

_, err = io.Copy(fw, zr)
if err1 := zr.Close(); err == nil {
err = err1
}

if errors.Is(done, err) {
err = nil
}
return err
}

```
rekulas
2024-03-16 14:00:31 +08:00
@lesismal 非常感谢 lesismal 大佬提供的方案, 测试确实可行, 我相信这也应该是最佳方案了, 也谢谢楼上其他的小伙伴们 🌹🌹🌹


@body007 我也试过 pipe, 这样确实可以解密但并不符合我想实现的目标, 因为在执行 zr, err := zlib.NewReader(ir)之前, 已经把 2 个 bin 文件数据合并到一起了, 而真实情况下 1 和 2 是分别解压的, 当然也可能我对 pipe 的理解不到位, 不清楚是否还有其他通过 pipe 方式实现流式解压的
lesismal
2024-03-16 15:18:04 +08:00
Welcome!

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1017220

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX