Похоже, проблема заключается в повторном вызове gzip.NewWriter()
в func(*CursorReader) Read([]byte) (int, error)
Вы выделяете новый gzip.Writer
для каждого вызова Read
. Сжатие gzip
с сохранением состояния, поэтому вы должны использовать только один экземпляр Writer
для всех операций.
Решение # 1
Довольно простым решением вашей проблемы будет прочитать все строки в курсоре и передать его через gzip.Writer
и сохранить сжатый контент в буфере в памяти.
var cursor, _ = collection.Find(context.TODO(), filter)
defer cursor.Close(context.TODO())
// prepare a buffer to hold gzipped data
var buffer bytes.Buffer
var gz = gzip.NewWriter(&buffer)
defer gz.Close()
for cursor.Next(context.TODO()) {
if _, err = io.WriteString(gz, cursor.Current.String()); err != nil {
// handle error somehow ¯\_(ツ)_/¯
}
}
// you can now use buffer as io.Reader
// and it'll contain gzipped data for your serialized rows
_, err = s3.Upload(&s3.UploadInput{
Bucket: aws.String("..."),
Key: aws.String("...")),
Body: &buffer,
})
Решение # 2
Другое решение будет использовать io.Pipe()
и goroutines для создания потока, который читает и сжимает данные по требованию, а не в буфере в памяти. Это полезно, если данные, которые вы читаете, достаточно велики, и вы не можете хранить все это в памяти.
var cursor, _ = collection.Find(context.TODO(), filter)
defer cursor.Close(context.TODO())
// create pipe endpoints
reader, writer := io.Pipe()
// note: io.Pipe() returns a synchronous in-memory pipe
// reads and writes block on one another
// make sure to go through docs once.
// now, since reads and writes on a pipe blocks
// we must move to a background goroutine else
// all our writes would block forever
go func() {
// order of defer here is important
// see: https://stackoverflow.com/a/24720120/6611700
// make sure gzip stream is closed before the pipe
// to ensure data is flushed properly
defer writer.Close()
var gz = gzip.NewWriter(writer)
defer gz.Close()
for cursor.Next(context.Background()) {
if _, err = io.WriteString(gz, cursor.Current.String()); err != nil {
// handle error somehow ¯\_(ツ)_/¯
}
}
}()
// you can now use reader as io.Reader
// and it'll contain gzipped data for your serialized rows
_, err = s3.Upload(&s3.UploadInput{
Bucket: aws.String("..."),
Key: aws.String("...")),
Body: reader,
})