【问题标题】:Decompress gzip string with golang用golang解压gzip字符串
【发布时间】:2019-04-10 00:02:21
【问题描述】:

我有一个包含 gzip 压缩字符串的字符串,因此没有文件头,标准 compress/gzip 库抛出错误 gzip: invalid header

如何在 go 中解压 gzip 压缩的字符串?

这就是我正在尝试的

nbody := "eNorTk0uSi0BAAjRAoc="
rdata := strings.NewReader(nbody)
r,err := gzip.NewReader(rdata)
log.Println(r)
if err != nil {
    log.Fatal(err)
}
s, _ := ioutil.ReadAll(r)
fmt.Println(string(s))

【问题讨论】:

  • eNorTk0uSi0BAAjRAoc= - 这不是“gzip 字符串”。这是 zlib 编码的(zlib 不是 gzip,它基本上只是 deflate 编码内容前面的不同标头),然后是另外的 base64 编码,所以你需要先从 base64 解码,然后使用 zlib 解压缩而不是gzip解压。
  • 我们是否可以重新命名这个问题,使其不会出现在谷歌搜索中?

标签: go gzip


【解决方案1】:

...我有一个包含 gzip 压缩字符串的字符串

nbody := "eNorTk0uSi0BAAjRAoc="

这不是“gzip 压缩字符串”。这看起来像是一些需要首先解码的 base64 编码数据。解码后它也不是 gzip 而是 zlib - 这与 gzip (使用 deflate 算法压缩的内容)基本相同,但文件头不同。因此尝试使用 gzip 对其进行解码是行不通的。

因此,以下内容将获取您的原始字符串,从 base64 解码并使用 zlib(不是 gzip)解压缩:

package main
  
import (
        "bytes"
        "compress/zlib"
        "encoding/base64"
        "fmt"
        "io/ioutil"
)

func main() {
        b64z := "eNorTk0uSi0BAAjRAoc="
        z, _ := base64.StdEncoding.DecodeString(b64z)
        r, _ := zlib.NewReader(bytes.NewReader(z))
        result, _ := ioutil.ReadAll(r)
        fmt.Println(string(result))  // results in "secret"
}

【讨论】:

  • 感谢您的回答,您是如何知道使用 zlib 的?输入实际上是由 Celery(一个 python 工具)压缩的,我表示我想要 gzip 压缩,但实际上你的解决方案有效,所以我想了解你是如何检测到它是一个 zlib 压缩字符串
  • @perrohunter:基数 64 很明显。所以我做了echo 'eNorTk0uSi0BAAjRAoc=' | base64 -d > f; file f 显示f: zlib compressed data
【解决方案2】:

既然这个问题一直出现在谷歌上,如果你在一个字符串中有真正的 gzip 编码数据并且想要解码它,你会这样做:

import "compress/gzip";
import "bytes";
import "io/ioutil";
...

original := "gzipencodeddata";

reader := bytes.NewReader([]byte(original))
gzreader, e1 := gzip.NewReader(reader);
if(e1 != nil){
    fmt.Println(e1); // Maybe panic here, depends on your error handling.
}

output, e2 := ioutil.ReadAll(gzreader);
if(e2 != nil){
    fmt.Println(e2);
}

result := string(output);

【讨论】:

    【解决方案3】:

    如果您有大量输入,您可能希望使用流和自定义解码器链。

    它的优点是(除了这个例子)编码的输入和解码的输出都不必驻留在 RAM 中。

    package main
    
    import (
        "bytes"
        "compress/zlib"
        "encoding/base64"
        "fmt"
        "io"
        "log"
        "os"
        "strings"
    )
    
    const nbody = "eNorTk0uSi0BAAjRAoc="
    
    func main() {
    
        _, err := io.Copy(os.Stdout, decoder(strings.NewReader(nbody)))
        if err != nil {
            log.Fatalf("Error copying decoded value to stdout: %s",err)
        }
    }
    
    // This could use any io.Reader as input, for example
    // a request body in http requests
    func decoder(r io.Reader) io.Reader {
    
        // We simply set up a custom chain of Decoders
        d, err := zlib.NewReader(
            base64.NewDecoder(base64.StdEncoding, r))
    
        // This should only occur if one of the Decoders can not reset
        // its internal buffer. Hence, it validates a panic.
        if err != nil {
            panic(fmt.Sprintf("Error setting up decoder chain: %s", err))
        }
    
        // We return an io.Reader which can be used as any other
        return d
    
    }
    

    Run on playground

    【讨论】:

    • 感谢您的建议,我会调查一下,但我的输入来自从 redis 中提取的 json blob,之后我可以将其转换为流,现在我从 Steffen 获得了解决方案乌尔里希的作品
    猜你喜欢
    • 1970-01-01
    • 2017-10-10
    • 1970-01-01
    • 2021-11-09
    • 1970-01-01
    • 2011-04-07
    • 1970-01-01
    • 2011-08-18
    • 1970-01-01
    相关资源
    最近更新 更多