compression.go 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. package util
  2. import (
  3. "bytes"
  4. "compress/flate"
  5. "compress/gzip"
  6. "fmt"
  7. "io/ioutil"
  8. "strings"
  9. "github.com/chrislusf/seaweedfs/weed/glog"
  10. "github.com/klauspost/compress/zstd"
  11. )
  12. func GzipData(input []byte) ([]byte, error) {
  13. buf := new(bytes.Buffer)
  14. w, _ := gzip.NewWriterLevel(buf, flate.BestSpeed)
  15. if _, err := w.Write(input); err != nil {
  16. glog.V(2).Infoln("error compressing data:", err)
  17. return nil, err
  18. }
  19. if err := w.Close(); err != nil {
  20. glog.V(2).Infoln("error closing compressed data:", err)
  21. return nil, err
  22. }
  23. return buf.Bytes(), nil
  24. }
  25. var zstdEncoder, _ = zstd.NewWriter(nil)
  26. func ZstdData(input []byte) ([]byte, error) {
  27. return zstdEncoder.EncodeAll(input, nil), nil
  28. }
  29. func DecompressData(input []byte) ([]byte, error) {
  30. if IsGzippedContent(input) {
  31. return ungzipData(input)
  32. }
  33. if IsZstdContent(input) {
  34. return unzstdData(input)
  35. }
  36. return input, fmt.Errorf("unsupported compression")
  37. }
  38. func ungzipData(input []byte) ([]byte, error) {
  39. buf := bytes.NewBuffer(input)
  40. r, _ := gzip.NewReader(buf)
  41. defer r.Close()
  42. output, err := ioutil.ReadAll(r)
  43. if err != nil {
  44. glog.V(2).Infoln("error uncompressing data:", err)
  45. }
  46. return output, err
  47. }
  48. var decoder, _ = zstd.NewReader(nil)
  49. func unzstdData(input []byte) ([]byte, error) {
  50. return decoder.DecodeAll(input, nil)
  51. }
  52. func IsGzippedContent(data []byte) bool {
  53. if len(data) < 2 {
  54. return false
  55. }
  56. return data[0] == 31 && data[1] == 139
  57. }
  58. func IsZstdContent(data []byte) bool {
  59. if len(data) < 4 {
  60. return false
  61. }
  62. return data[3] == 0xFD && data[2] == 0x2F && data[1] == 0xB5 && data[0] == 0x28
  63. }
  64. /*
  65. * Default not to compressed since compression can be done on client side.
  66. */func IsCompressableFileType(ext, mtype string) (shouldBeCompressed, iAmSure bool) {
  67. // text
  68. if strings.HasPrefix(mtype, "text/") {
  69. return true, true
  70. }
  71. // images
  72. switch ext {
  73. case ".svg", ".bmp", ".wav":
  74. return true, true
  75. }
  76. if strings.HasPrefix(mtype, "image/") {
  77. return false, true
  78. }
  79. // by file name extension
  80. switch ext {
  81. case ".zip", ".rar", ".gz", ".bz2", ".xz", ".zst":
  82. return false, true
  83. case ".pdf", ".txt", ".html", ".htm", ".css", ".js", ".json":
  84. return true, true
  85. case ".php", ".java", ".go", ".rb", ".c", ".cpp", ".h", ".hpp":
  86. return true, true
  87. case ".png", ".jpg", ".jpeg":
  88. return false, true
  89. }
  90. // by mime type
  91. if strings.HasPrefix(mtype, "application/") {
  92. if strings.HasSuffix(mtype, "zstd") {
  93. return false, true
  94. }
  95. if strings.HasSuffix(mtype, "xml") {
  96. return true, true
  97. }
  98. if strings.HasSuffix(mtype, "script") {
  99. return true, true
  100. }
  101. }
  102. if strings.HasPrefix(mtype, "audio/") {
  103. switch strings.TrimPrefix(mtype, "audio/") {
  104. case "wave", "wav", "x-wav", "x-pn-wav":
  105. return true, true
  106. }
  107. }
  108. return false, false
  109. }