compress.go 2.5 KB

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