Распаковать строку gzip с помощью golang

#go #gzip

#Вперед #gzip

Вопрос:

У меня есть строка, которая содержит сжатую строку gzip, поэтому заголовков файлов нет, стандартная compress/gzip библиотека выдает ошибку gzip: invalid header

Как я могу распаковать сжатую строку gzip в go?

Это то, что я пытаюсь

 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))
  

Комментарии:

1. eNorTk0uSi0BAAjRAoc= — это не «строка gzip». Это закодировано в zlib (zlib — это не gzip, также это в основном просто другой заголовок перед закодированным в deflate содержимым), а затем дополнительно кодируется в base64, поэтому вам нужно сначала декодировать его из base64, а затем использовать декомпрессию zlib, а не декомпрессию gzip.

2. Можем ли мы каким-то образом изменить заголовок этого вопроса, чтобы он не отображался в поиске Google?

3. Можно ли полностью удалить этот вопрос или что-то в этом роде? Это не gzip

Ответ №1:

Поскольку этот вопрос постоянно появляется в Google, если у вас есть реальные данные, закодированные в 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);
  

Ответ №2:

… У меня есть строка, которая содержит сжатую строку 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"
}
  

Комментарии:

1. Спасибо за ваш ответ, как вы узнали, что нужно использовать zlib? входные данные фактически сжимаются Celery (инструментом python), где я указываю, что хочу сжатие gzip, но на самом деле ваше решение сработало, поэтому я хочу понять, как вы обнаружили, что это сжатая строка zlib

2. @perrohunter: базовый 64 был очевиден. Итак, я сделал echo 'eNorTk0uSi0BAAjRAoc=' | base64 -d > f; file f то, что показал f: zlib compressed data .

Ответ №3:

Если у вас большие входные данные, вы можете вместо этого использовать потоки и пользовательскую цепочку декодеров.

Преимущество заключается в том, что (за исключением этого примера) ни закодированный ввод, ни декодированный вывод не должны находиться в оперативной памяти.

 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

Комментарии:

1. Спасибо за предложение, я рассмотрю это, но мой ввод поступает из большого двоичного объекта json, который извлекается из redis, я могу впоследствии превратить его в поток, а пока я получил решение от Штеффена Ульриха, которое работает