【问题标题】:What is a correct way of writing data in gzip format?以 gzip 格式写入数据的正确方法是什么?
【发布时间】:2021-08-11 03:39:06
【问题描述】:

我的应用程序产生了大量的文本数据,为了减少磁盘消耗我想以 gzip 格式写入数据

许多 goroutine 同时调用 WriteData() 函数。

但是 linux gzip 抱怨文件损坏。

zcat ./2021-08-11-00.gz > /dev/null
gzip: ./2021-08-11-00.gz: invalid compressed data--format violated

它不是每次都发生,而是几乎每个第二次写入的文件都发生。

我的代码有什么问题?

我的 DataWrite 包看起来像

package storage

import (
    "compress/gzip"
    "os"
    "sync"

    "github.com/rs/zerolog/log"
)

type Storage struct {
    handle *os.File
    writer *gzip.Writer

    lock sync.Mutex
}

func (s *Storage) Init(filename string) error {

    file, err := os.OpenFile(filename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)

    if err != nil {
        return err
    }

    s.handle = file
    s.writer = gzip.NewWriter(file)

    return nil
}

func (s *Storage) Shutdown() {

    if err := s.writer.Close(); err != nil {
        log.Warn().Err(err).Msg("Gzip writer close failed")
    }

    if err := s.handle.Close(); err != nil {
        log.Warn().Err(err).Msg("Gzip handle close failed")
    }
}

func (s *Storage) WriteData(data *MyStruct) error {

    s.lock.Lock()
    defer s.lock.Unlock()

    buffer := data.content

    _, err := s.writer.Write([]byte(buffer))

    if err != nil {
        log.Warn().Err(err).Msg("Gzip write failed")
        return err
    }

    if err := s.writer.Flush(); err != nil {
        return err
    }

    if err := s.handle.Sync(); err != nil {
        return err
    }

    return nil
}

【问题讨论】:

  • 你可能不应该使用O_APPEND。我不知道它是否会导致您的问题(我没有看到明显的方法,知道 gzip 编写器是如何工作的),但这也不正确。它有效地使文件不可搜索,并强制所有写入都在末尾。
  • buffer 定义在哪里?
  • 这个程序充满了编译错误。 @AlexBlack 请粘贴一个最小的可重现程序。
  • 你应该可以写出一个可复现的例子
  • 我已经更新了代码sn-p,

标签: go gzip


【解决方案1】:

您没有同步关机和写入。

package storage

type Storage struct {
    handle *os.File
    writer *gzip.Writer

    lock sync.Mutex
}

func (s *Storage) Init(filename string) {

    file, err := os.OpenFile(filename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)

    if err != nil {
        return err
    }

    s.handle = file
    s.writer = gzip.NewWriter(file)

}

func (s *Storage) Shutdown() {

    s.lock.Lock() // Here !!
    defer s.lock.Unlock()

    if err := s.writer.Close(); err != nil {
        log.Warn().Err(err).Str("fileName", path).Msg("Gzip writer close failed")
    }

    if err := s.handle.Close(); err != nil {
        log.Warn().Err(err).Str("fileName", path).Msg("Gzip handle close failed")
    }
}

func (s *Storage) WriteData(data *MyStruct) error {

    s.lock.Lock()
    defer s.lock.Unlock()

    cnt, err := s.writer.Write([]byte(buffer))

    if err != nil {
        log.Warn().Err(err).Msg("Gzip write failed")
        return err
    }

    if err := s.writer.Flush(); err != nil {
        return err
    }

    if err := s.handle.Sync(); err != nil {
        return err
    }

    return nil
}

【讨论】:

    【解决方案2】:

    在这里您可以看到以下 gzip 压缩的工作代码:

    package main
    
    import (
        "compress/gzip"
        "log"
        "os"
        "time"
        "sync"
    )
    
    
    type Storage struct {
        handle *os.File
        writer *gzip.Writer
        buffer []byte
        lock sync.Mutex
        Name string
        Comment string
        ModTime time.Time
    }
    
    func (s *Storage) Init(filename string) {
        file, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY, 0644)
        if err != nil {
            log.Fatal(err)
        }
    
        s.handle = file
        s.writer = gzip.NewWriter(file)
        s.Name = "a-new-hope.txt"
        s.Comment = "an epic space opera by George Lucas"
        s.ModTime = time.Date(1977, time.May, 25, 0, 0, 0, 0, time.UTC)
        s.buffer = []byte("Hello")
    }
    
    func (s *Storage) Shutdown() {
    
        if err := s.writer.Close(); err != nil {
            log.Fatal("Gzip writer close failed")
        }
    
        if err := s.handle.Close(); err != nil {
            log.Fatal("Gzip writer close failed")
        }
    }
    
    func (s *Storage) WriteData() error {
    
        s.lock.Lock()
        defer s.lock.Unlock()
    
        _, err := s.writer.Write([]byte(s.buffer))
    
        if err != nil {
            log.Fatal("Gzip write failed")
            return err
        }
    
        if err := s.writer.Flush(); err != nil {
            return err
        }
    
        if err := s.handle.Sync(); err != nil {
            return err
        }
    
        return nil
    }
    
    func main() {
        //WriteGzip("test.gzip", "My data")
        s := Storage{};
        s.Init("sss.gzip");
        s.WriteData();
        s.Shutdown();
    }
    
    

    编辑 修改后使其与相关代码相似,几乎没有变化。 WriteData 从 Storage 结构中获取缓冲区,因为 MyStruct 不在代码中。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-06-07
      • 1970-01-01
      • 1970-01-01
      • 2015-09-02
      • 1970-01-01
      • 2020-05-15
      • 1970-01-01
      • 2018-08-28
      相关资源
      最近更新 更多