gres_func_zip.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. // Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
  2. //
  3. // This Source Code Form is subject to the terms of the MIT License.
  4. // If a copy of the MIT was not distributed with this file,
  5. // You can obtain one at https://github.com/gogf/gf.
  6. package gres
  7. import (
  8. "archive/zip"
  9. "context"
  10. "github.com/gogf/gf/internal/fileinfo"
  11. "github.com/gogf/gf/internal/intlog"
  12. "github.com/gogf/gf/os/gfile"
  13. "github.com/gogf/gf/text/gregex"
  14. "io"
  15. "os"
  16. "strings"
  17. "time"
  18. )
  19. // ZipPathWriter compresses <paths> to <writer> using zip compressing algorithm.
  20. // The unnecessary parameter <prefix> indicates the path prefix for zip file.
  21. //
  22. // Note that the parameter <paths> can be either a directory or a file, which
  23. // supports multiple paths join with ','.
  24. func zipPathWriter(paths string, writer io.Writer, prefix ...string) error {
  25. zipWriter := zip.NewWriter(writer)
  26. defer zipWriter.Close()
  27. for _, path := range strings.Split(paths, ",") {
  28. path = strings.TrimSpace(path)
  29. if err := doZipPathWriter(path, "", zipWriter, prefix...); err != nil {
  30. return err
  31. }
  32. }
  33. return nil
  34. }
  35. // doZipPathWriter compresses the file of given <path> and writes the content to <zipWriter>.
  36. // The parameter <exclude> specifies the exclusive file path that is not compressed to <zipWriter>,
  37. // commonly the destination zip file path.
  38. // The unnecessary parameter <prefix> indicates the path prefix for zip file.
  39. func doZipPathWriter(path string, exclude string, zipWriter *zip.Writer, prefix ...string) error {
  40. var (
  41. err error
  42. files []string
  43. )
  44. path, err = gfile.Search(path)
  45. if err != nil {
  46. return err
  47. }
  48. if gfile.IsDir(path) {
  49. files, err = gfile.ScanDir(path, "*", true)
  50. if err != nil {
  51. return err
  52. }
  53. } else {
  54. files = []string{path}
  55. }
  56. headerPrefix := ""
  57. if len(prefix) > 0 && prefix[0] != "" {
  58. headerPrefix = prefix[0]
  59. }
  60. headerPrefix = strings.TrimRight(headerPrefix, `\/`)
  61. if len(headerPrefix) > 0 && gfile.IsDir(path) {
  62. headerPrefix += "/"
  63. }
  64. if headerPrefix == "" {
  65. headerPrefix = gfile.Basename(path)
  66. }
  67. headerPrefix = strings.Replace(headerPrefix, `//`, `/`, -1)
  68. for _, file := range files {
  69. if exclude == file {
  70. intlog.Printf(context.TODO(), `exclude file path: %s`, file)
  71. continue
  72. }
  73. err = zipFile(file, headerPrefix+gfile.Dir(file[len(path):]), zipWriter)
  74. if err != nil {
  75. return err
  76. }
  77. }
  78. // Add all directories to zip archive.
  79. if headerPrefix != "" {
  80. var name string
  81. path = headerPrefix
  82. for {
  83. name = strings.Replace(gfile.Basename(path), `\`, `/`, -1)
  84. err = zipFileVirtual(
  85. fileinfo.New(name, 0, os.ModeDir|os.ModePerm, time.Now()), path, zipWriter,
  86. )
  87. if err != nil {
  88. return err
  89. }
  90. if path == `/` || !strings.Contains(path, `/`) {
  91. break
  92. }
  93. path = gfile.Dir(path)
  94. }
  95. }
  96. return nil
  97. }
  98. // zipFile compresses the file of given <path> and writes the content to <zw>.
  99. // The parameter <prefix> indicates the path prefix for zip file.
  100. func zipFile(path string, prefix string, zw *zip.Writer) error {
  101. prefix = strings.Replace(prefix, `//`, `/`, -1)
  102. file, err := os.Open(path)
  103. if err != nil {
  104. return nil
  105. }
  106. defer file.Close()
  107. info, err := file.Stat()
  108. if err != nil {
  109. return err
  110. }
  111. header, err := createFileHeader(info, prefix)
  112. if err != nil {
  113. return err
  114. }
  115. if !info.IsDir() {
  116. header.Method = zip.Deflate
  117. }
  118. writer, err := zw.CreateHeader(header)
  119. if err != nil {
  120. return err
  121. }
  122. if !info.IsDir() {
  123. if _, err = io.Copy(writer, file); err != nil {
  124. return err
  125. }
  126. }
  127. return nil
  128. }
  129. func zipFileVirtual(info os.FileInfo, path string, zw *zip.Writer) error {
  130. header, err := createFileHeader(info, "")
  131. if err != nil {
  132. return err
  133. }
  134. header.Name = path
  135. if _, err = zw.CreateHeader(header); err != nil {
  136. return err
  137. }
  138. return nil
  139. }
  140. func createFileHeader(info os.FileInfo, prefix string) (*zip.FileHeader, error) {
  141. header, err := zip.FileInfoHeader(info)
  142. if err != nil {
  143. return nil, err
  144. }
  145. if len(prefix) > 0 {
  146. header.Name = prefix + `/` + header.Name
  147. header.Name = strings.Replace(header.Name, `\`, `/`, -1)
  148. header.Name, _ = gregex.ReplaceString(`/{2,}`, `/`, header.Name)
  149. }
  150. return header, nil
  151. }