Закрытие gzip writer в отложенном состоянии приводит к потере данных - PullRequest
0 голосов
/ 30 марта 2020

Я использую golang gzip.NewWriter, чтобы сжать фрагмент , и defer Close(), чтобы закрыть писатель. Но при чтении из заархивированных данных, он вернет unexpected EOF. Код:

func main() {

    a := []byte{'a', 'b', 'c', 'd', 'e', 'f'}
    zippedData, err := zipData(a)
    if err != nil {
        panic(err)
    }

    unzippedData, err := unzipData(zippedData)
    if err != nil {
        panic(err)
    }
    fmt.Printf("%v\n", unzippedData)

}


Функция zip:

func zipData(originData []byte) ([]byte, error) {
    var bf bytes.Buffer
    gw := gzip.NewWriter(&bf)

    defer gw.Close()

    _, err := gw.Write(originData)
    if err != nil {
        return nil, errors.New(fmt.Sprintf("gzip data err: %v", err))
    }

    err = gw.Flush()
    if err != nil {
        return nil, err
    }
    // if I rm 'defer gw.Close()' and call 'gw.Close()' here, it'll be ok

    logs.Debug("before gzip len: %v", len(originData))
    logs.Debug("gzip len: %v", bf.Len())
    return bf.Bytes(), nil
}

Выше функция zip использует defer gw.Close() для закрытия gw.

Функция распаковки:

func unzipData(zippedData []byte) ([]byte, error) {
    dst := make([]byte, len(zippedData))
    copy(dst, zippedData)

    reader, err := gzip.NewReader(bytes.NewBuffer(dst))
    if err != nil {
        return nil, errors.New(fmt.Sprintf("unzip err :%v", err))
    }

    defer reader.Close()

    data, err := ioutil.ReadAll(reader)
    if err != nil {
        return nil, errors.New(fmt.Sprintf("read err :%v", err))
    }
    return data, err
}

Почему defer gw.Close() чехлы unexpected EOF?

Ответы [ 2 ]

4 голосов
/ 30 марта 2020

При отсрочке вам не хватает нижнего колонтитула gzip. Согласно Close документам:

Close закрывает Writer, сбрасывая все неписанные данные в базовый io.Writer и записывая нижний колонтитул GZIP. Он не закрывает базовый io.Writer.

Так что, хотя Flush сбрасывает любые буферизованные данные, он не записывает нижний колонтитул. При отложенном закрытии вы получаете байтовый массив, который не содержит нижний колонтитул, и возвращаете его, а затем нижний колонтитул записывается в вывод.

Закройте средство записи перед возвратом.

3 голосов
/ 30 марта 2020

С defer, gw.Close() запускается после вызова bf.Bytes() в операторе возврата. Чтобы убедиться, что все содержимое возвращено, вы должны явно вызвать gw.Close, прежде чем пытаться прочитать байты из буфера.

Самое простое исправление для вашего кода - заменить вызов Flush на вызов Close , Flush предназначен для случаев, когда вы еще не закончили писать, но в вашем случае вы уже закончили сжатие, поэтому достаточно вызвать Close.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...