【问题标题】:How to compress and decompress a file using lz4?如何使用 lz4 压缩和解压文件?
【发布时间】:2016-05-01 16:48:02
【问题描述】:

我想在 Go 中使用 lz4 算法压缩和解压缩文件。有没有可用的软件包来做到这一点? 我搜索并找到了一个名为 https://github.com/pierrec/lz4

我是 Go 新手,不知道如何使用这个包来压缩和解压文件。

我需要使用这个包将文件压缩成二进制格式,然后使用 Go 将二进制文件解压成原始文件。

【问题讨论】:

    标签: go compression lz4


    【解决方案1】:

    我认为打击示例应该指导您正确的方向。这是使用github.com/pierrec/lz4包进行压缩和解压的最简单示例。

    //compress project main.go
    package main
    
    import "fmt"
    import "github.com/pierrec/lz4"
    
    var fileContent = `CompressBlock compresses the source buffer starting at soffet into the destination one.
    This is the fast version of LZ4 compression and also the default one.
    The size of the compressed data is returned. If it is 0 and no error, then the data is incompressible.
    An error is returned if the destination buffer is too small.`
    
    func main() {
        toCompress := []byte(fileContent)
        compressed := make([]byte, len(toCompress))
    
        //compress
        l, err := lz4.CompressBlock(toCompress, compressed, 0)
        if err != nil {
            panic(err)
        }
        fmt.Println("compressed Data:", string(compressed[:l]))
    
        //decompress
        decompressed := make([]byte, len(toCompress))
        l, err = lz4.UncompressBlock(compressed[:l], decompressed, 0)
        if err != nil {
            panic(err)
        }
        fmt.Println("\ndecompressed Data:", string(decompressed[:l]))
    }
    

    【讨论】:

    • 我需要使用这个包将一个文件压缩成其他二进制文件。我还使用这个主程序来压缩我的文件 [github.com/pierrec/lz4/blob/master/lz4c/main.go]。它正在被压缩..
    • 感谢您的回答。如果我在两个不同的函数中编写压缩和解压缩,那么我的解压缩部分只有压缩数据。那么如何在创建解压缓冲区时处理这种情况decompressed := make([]byte, len(toCompress)) 而且我也得到了短缓冲区错误,我知道缓冲区不够。请帮帮我....
    • 您需要在压缩时将未压缩的数据大小存储在某处,并在解压缩时使用它。例如在 db 或压缩数据本身中。
    • 您不需要存储未压缩的数据大小。只需在您仍有长度的情况下修剪压缩数据切片的创建点:l, err := lz4.CompressBlock(toCompress, compressed, []int{0}) | compressed = compressed[:l]然后就可以直接使用解压了l, err = lz4.UncompressBlock(compressed, decompressed)
    【解决方案2】:

    使用 bufio 包,您可以(解)压缩文件,而不会一次将其全部内容全部塞入您的内存中。

    实际上,这允许您(解)压缩大于系统可用内存的文件,这可能与您的具体情况相关,也可能不相关。

    如果这是相关的,您可以在这里找到一个工作示例:

    package main
    
    import (
        "bufio"
        "io"
        "os"
    
        "github.com/pierrec/lz4"
    )
    
    // Compress a file, then decompress it again!
    func main() {
        compress("./compress-me.txt", "./compressed.txt")
        decompress("./compressed.txt", "./decompressed.txt")
    }
    
    func compress(inputFile, outputFile string) {
        // open input file
        fin, err := os.Open(inputFile)
        if err != nil {
            panic(err)
        }
        defer func() {
            if err := fin.Close(); err != nil {
                panic(err)
            }
        }()
        // make a read buffer
        r := bufio.NewReader(fin)
    
        // open output file
        fout, err := os.Create(outputFile)
        if err != nil {
            panic(err)
        }
        defer func() {
            if err := fout.Close(); err != nil {
                panic(err)
            }
        }()
        // make an lz4 write buffer
        w := lz4.NewWriter(fout)
    
        // make a buffer to keep chunks that are read
        buf := make([]byte, 1024)
        for {
            // read a chunk
            n, err := r.Read(buf)
            if err != nil && err != io.EOF {
                panic(err)
            }
            if n == 0 {
                break
            }
    
            // write a chunk
            if _, err := w.Write(buf[:n]); err != nil {
                panic(err)
            }
        }
    
        if err = w.Flush(); err != nil {
            panic(err)
        }
    }
    
    func decompress(inputFile, outputFile string) {
        // open input file
        fin, err := os.Open(inputFile)
        if err != nil {
            panic(err)
        }
        defer func() {
            if err := fin.Close(); err != nil {
                panic(err)
            }
        }()
    
        // make an lz4 read buffer
        r := lz4.NewReader(fin)
    
        // open output file
        fout, err := os.Create(outputFile)
        if err != nil {
            panic(err)
        }
        defer func() {
            if err := fout.Close(); err != nil {
                panic(err)
            }
        }()
    
        // make a write buffer
        w := bufio.NewWriter(fout)
    
        // make a buffer to keep chunks that are read
        buf := make([]byte, 1024)
        for {
            // read a chunk
            n, err := r.Read(buf)
            if err != nil && err != io.EOF {
                panic(err)
            }
            if n == 0 {
                break
            }
    
            // write a chunk
            if _, err := w.Write(buf[:n]); err != nil {
                panic(err)
            }
        }
    
        if err = w.Flush(); err != nil {
            panic(err)
        }
    }
    

    【讨论】:

    • 它可以工作,但文件大小有点大 [google.com/…]。使用这个主代码,我完成了压缩。此压缩大小是输出大小的一半。
    • 我不完全理解您在该评论中要表达的意思,能否请您为我重新措辞?
    • 从您的代码中,我的测试文件被压缩到 13 KB,但是使用 [google.com/…] 这个主代码,同一个文件被压缩到 7 kb
    【解决方案3】:

    我所期望的结果来自下面的代码。我从这个文件中得到了这个 [https://www.google.com/url?q=https%3A%2F%2Fgithub.com%2Fpierrec%2Flz4%2Fblob%2Fmaster%2Flz4c%2Fmain.go&sa=D&sntz=1&usg=AFQjCNFIT2O1Grs0vu4Gh8Af96GSBaa9EA]。 文件在命令行参数中作为输入给出,并且它的压缩/解压缩成功。

    package main
    import (
        //  "bytes"
    
        "flag"
        "fmt"
        "io"
        "log"
        "os"
        "path"
        "runtime"
        "strings"
    
        "github.com/pierrec/lz4"
    )
    
    func main() {
        // Process command line arguments
        var (
            blockMaxSizeDefault = 4 << 20
            flagStdout          = flag.Bool("c", false, "output to stdout")
            flagDecompress      = flag.Bool("d", false, "decompress flag")
            flagBlockMaxSize    = flag.Int("B", blockMaxSizeDefault, "block max size [64Kb,256Kb,1Mb,4Mb]")
            flagBlockDependency = flag.Bool("BD", false, "enable block dependency")
            flagBlockChecksum   = flag.Bool("BX", false, "enable block checksum")
            flagStreamChecksum  = flag.Bool("Sx", false, "disable stream checksum")
            flagHighCompression = flag.Bool("9", false, "enabled high compression")
        )
        flag.Usage = func() {
            fmt.Fprintf(os.Stderr, "Usage:\n\t%s [arg] [input]...\n\tNo input means [de]compress stdin to stdout\n\n", os.Args[0])
            flag.PrintDefaults()
        }
        flag.Parse()
        fmt.Println("output to stdout ", *flagStdout)
        fmt.Println("Decompress", *flagDecompress)
        // Use all CPUs
        runtime.GOMAXPROCS(runtime.NumCPU())
    
        zr := lz4.NewReader(nil)
        zw := lz4.NewWriter(nil)
        zh := lz4.Header{
            BlockDependency: *flagBlockDependency,
            BlockChecksum:   *flagBlockChecksum,
            BlockMaxSize:    *flagBlockMaxSize,
            NoChecksum:      *flagStreamChecksum,
            HighCompression: *flagHighCompression,
        }
    
        worker := func(in io.Reader, out io.Writer) {
            if *flagDecompress {
                fmt.Println("\n Decompressing the data")
                zr.Reset(in)
                if _, err := io.Copy(out, zr); err != nil {
                    log.Fatalf("Error while decompressing input: %v", err)
                }
            } else {
                zw.Reset(out)
                zw.Header = zh
                if _, err := io.Copy(zw, in); err != nil {
                    log.Fatalf("Error while compressing input: %v", err)
                }
            }
        }
    
        // No input means [de]compress stdin to stdout
        if len(flag.Args()) == 0 {
            worker(os.Stdin, os.Stdout)
            os.Exit(0)
        }
    
        // Compress or decompress all input files
        for _, inputFileName := range flag.Args() {
            outputFileName := path.Clean(inputFileName)
    
            if !*flagStdout {
                if *flagDecompress {
                    outputFileName = strings.TrimSuffix(outputFileName, lz4.Extension)
                    if outputFileName == inputFileName {
                        log.Fatalf("Invalid output file name: same as input: %s", inputFileName)
                    }
                } else {
                    outputFileName += lz4.Extension
                }
            }
    
            inputFile, err := os.Open(inputFileName)
            if err != nil {
                log.Fatalf("Error while opening input: %v", err)
            }
    
            outputFile := os.Stdout
            if !*flagStdout {
                outputFile, err = os.Create(outputFileName)
                if err != nil {
                    log.Fatalf("Error while opening output: %v", err)
                }
            }
            worker(inputFile, outputFile)
    
            inputFile.Close()
            if !*flagStdout {
                outputFile.Close()
            }
        }
    }
    

    示例输入

    运行 compress.go -9=true sample.txt

    【讨论】:

      【解决方案4】:

      我也是 Go 新手,在使用 github.com/pierrec/lz4 时遇到了一些困难。

      我的误解是在NewWriter 上调用Close()不是可选的,如果不这样做会导致不正确的结果。 (我花了很多时间把我的头撞在墙上,认为这是可选的,只是一个最佳实践,因为它是关闭文件处理程序、网络连接等)

      我为压缩/解压缩编写了两个包装器版本。

      首先,一种通用的读取器/写入器方法(类似于 README 中的示例,但没有管道)[playground]:

      func compress(r io.Reader, w io.Writer) error {
          zw := lz4.NewWriter(w)
          _, err := io.Copy(zw, r)
          if err != nil {
              return err
          }
          // Closing is *very* important
          return zw.Close()
      }
      
      func decompress(r io.Reader, w io.Writer) error {
          zr := lz4.NewReader(r)
          _, err := io.Copy(w, zr)
          return err
      }
      

      如果您的数据量很小,并且您不需要/不想弄乱缓冲区并且只想输入未压缩的字节,输出压缩的字节,(以更“功能”的方式)第二个版本可能更多方便[playground]:

      func compress(in []byte) ([]byte, error) {
          r := bytes.NewReader(in)
          w := &bytes.Buffer{}
          zw := lz4.NewWriter(w)
          _, err := io.Copy(zw, r)
          if err != nil {
              return nil, err
          }
          // Closing is *very* important
          if err := zw.Close(); err != nil {
              return nil, err
          }
          return w.Bytes(), nil
      }
      
      func decompress(in []byte) ([]byte, error) {
          r := bytes.NewReader(in)
          w := &bytes.Buffer{}
          zr := lz4.NewReader(r)
          _, err := io.Copy(w, zr)
          if err != nil {
              return nil, err
          }
          return w.Bytes(), nil
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-03-31
        • 1970-01-01
        • 1970-01-01
        • 2021-10-03
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多