compress.go 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. package main
  2. import (
  3. "flag"
  4. "fmt"
  5. "io"
  6. "os"
  7. "sync/atomic"
  8. "code.cloudfoundry.org/bytefmt"
  9. "github.com/schollz/progressbar"
  10. "github.com/pierrec/cmdflag"
  11. "github.com/pierrec/lz4"
  12. )
  13. // Compress compresses a set of files or from stdin to stdout.
  14. func Compress(fs *flag.FlagSet) cmdflag.Handler {
  15. var blockMaxSize string
  16. fs.StringVar(&blockMaxSize, "size", "4M", "block max size [64K,256K,1M,4M]")
  17. var blockChecksum bool
  18. fs.BoolVar(&blockChecksum, "bc", false, "enable block checksum")
  19. var streamChecksum bool
  20. fs.BoolVar(&streamChecksum, "sc", false, "disable stream checksum")
  21. var level int
  22. fs.IntVar(&level, "l", 0, "compression level (0=fastest)")
  23. var concurrency int
  24. fs.IntVar(&concurrency, "c", -1, "concurrency (default=all CPUs")
  25. return func(args ...string) (int, error) {
  26. sz, err := bytefmt.ToBytes(blockMaxSize)
  27. if err != nil {
  28. return 0, err
  29. }
  30. zw := lz4.NewWriter(nil)
  31. zw.Header = lz4.Header{
  32. BlockChecksum: blockChecksum,
  33. BlockMaxSize: int(sz),
  34. NoChecksum: streamChecksum,
  35. CompressionLevel: level,
  36. }
  37. zw.WithConcurrency(concurrency)
  38. // Use stdin/stdout if no file provided.
  39. if len(args) == 0 {
  40. zw.Reset(os.Stdout)
  41. _, err := io.Copy(zw, os.Stdin)
  42. if err != nil {
  43. return 0, err
  44. }
  45. return 0, zw.Close()
  46. }
  47. for fidx, filename := range args {
  48. // Input file.
  49. file, err := os.Open(filename)
  50. if err != nil {
  51. return fidx, err
  52. }
  53. finfo, err := file.Stat()
  54. if err != nil {
  55. return fidx, err
  56. }
  57. mode := finfo.Mode() // use the same mode for the output file
  58. // Accumulate compressed bytes num.
  59. var (
  60. zsize int64
  61. size = finfo.Size()
  62. )
  63. if size > 0 {
  64. // Progress bar setup.
  65. numBlocks := int(size) / zw.Header.BlockMaxSize
  66. bar := progressbar.NewOptions(numBlocks,
  67. // File transfers are usually slow, make sure we display the bar at 0%.
  68. progressbar.OptionSetRenderBlankState(true),
  69. // Display the filename.
  70. progressbar.OptionSetDescription(filename),
  71. progressbar.OptionClearOnFinish(),
  72. )
  73. zw.OnBlockDone = func(n int) {
  74. _ = bar.Add(1)
  75. atomic.AddInt64(&zsize, int64(n))
  76. }
  77. }
  78. // Output file.
  79. zfilename := fmt.Sprintf("%s%s", filename, lz4.Extension)
  80. zfile, err := os.OpenFile(zfilename, os.O_CREATE|os.O_WRONLY, mode)
  81. if err != nil {
  82. return fidx, err
  83. }
  84. zw.Reset(zfile)
  85. // Compress.
  86. _, err = io.Copy(zw, file)
  87. if err != nil {
  88. return fidx, err
  89. }
  90. for _, c := range []io.Closer{zw, zfile} {
  91. err := c.Close()
  92. if err != nil {
  93. return fidx, err
  94. }
  95. }
  96. if size > 0 {
  97. fmt.Printf("%s %.02f%%\n", zfilename, float64(zsize)*100/float64(size))
  98. }
  99. }
  100. return len(args), nil
  101. }
  102. }