gcompress_zip.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  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 gcompress
  7. import (
  8. "archive/zip"
  9. "bytes"
  10. "context"
  11. "github.com/gogf/gf/internal/intlog"
  12. "github.com/gogf/gf/os/gfile"
  13. "github.com/gogf/gf/text/gstr"
  14. "io"
  15. "os"
  16. "path/filepath"
  17. "strings"
  18. )
  19. // ZipPath compresses <paths> to <dest> 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 ZipPath(paths, dest string, prefix ...string) error {
  25. writer, err := os.Create(dest)
  26. if err != nil {
  27. return err
  28. }
  29. defer writer.Close()
  30. zipWriter := zip.NewWriter(writer)
  31. defer zipWriter.Close()
  32. for _, path := range strings.Split(paths, ",") {
  33. path = strings.TrimSpace(path)
  34. if err := doZipPathWriter(path, gfile.RealPath(dest), zipWriter, prefix...); err != nil {
  35. return err
  36. }
  37. }
  38. return nil
  39. }
  40. // ZipPathWriter compresses <paths> to <writer> using zip compressing algorithm.
  41. // The unnecessary parameter <prefix> indicates the path prefix for zip file.
  42. //
  43. // Note that the parameter <paths> can be either a directory or a file, which
  44. // supports multiple paths join with ','.
  45. func ZipPathWriter(paths string, writer io.Writer, prefix ...string) error {
  46. zipWriter := zip.NewWriter(writer)
  47. defer zipWriter.Close()
  48. for _, path := range strings.Split(paths, ",") {
  49. path = strings.TrimSpace(path)
  50. if err := doZipPathWriter(path, "", zipWriter, prefix...); err != nil {
  51. return err
  52. }
  53. }
  54. return nil
  55. }
  56. // doZipPathWriter compresses the file of given <path> and writes the content to <zipWriter>.
  57. // The parameter <exclude> specifies the exclusive file path that is not compressed to <zipWriter>,
  58. // commonly the destination zip file path.
  59. // The unnecessary parameter <prefix> indicates the path prefix for zip file.
  60. func doZipPathWriter(path string, exclude string, zipWriter *zip.Writer, prefix ...string) error {
  61. var err error
  62. var files []string
  63. path, err = gfile.Search(path)
  64. if err != nil {
  65. return err
  66. }
  67. if gfile.IsDir(path) {
  68. files, err = gfile.ScanDir(path, "*", true)
  69. if err != nil {
  70. return err
  71. }
  72. } else {
  73. files = []string{path}
  74. }
  75. headerPrefix := ""
  76. if len(prefix) > 0 && prefix[0] != "" {
  77. headerPrefix = prefix[0]
  78. }
  79. headerPrefix = strings.TrimRight(headerPrefix, "\\/")
  80. if gfile.IsDir(path) {
  81. if len(headerPrefix) > 0 {
  82. headerPrefix += "/"
  83. } else {
  84. headerPrefix = gfile.Basename(path)
  85. }
  86. }
  87. headerPrefix = strings.Replace(headerPrefix, "//", "/", -1)
  88. for _, file := range files {
  89. if exclude == file {
  90. intlog.Printf(context.TODO(), `exclude file path: %s`, file)
  91. continue
  92. }
  93. dir := gfile.Dir(file[len(path):])
  94. if dir == "." {
  95. dir = ""
  96. }
  97. err := zipFile(file, headerPrefix+dir, zipWriter)
  98. if err != nil {
  99. return err
  100. }
  101. }
  102. return nil
  103. }
  104. // UnZipFile decompresses <archive> to <dest> using zip compressing algorithm.
  105. // The optional parameter <path> specifies the unzipped path of <archive>,
  106. // which can be used to specify part of the archive file to unzip.
  107. //
  108. // Note that the parameter <dest> should be a directory.
  109. func UnZipFile(archive, dest string, path ...string) error {
  110. readerCloser, err := zip.OpenReader(archive)
  111. if err != nil {
  112. return err
  113. }
  114. defer readerCloser.Close()
  115. return unZipFileWithReader(&readerCloser.Reader, dest, path...)
  116. }
  117. // UnZipContent decompresses <data> to <dest> using zip compressing algorithm.
  118. // The parameter <path> specifies the unzipped path of <archive>,
  119. // which can be used to specify part of the archive file to unzip.
  120. //
  121. // Note that the parameter <dest> should be a directory.
  122. func UnZipContent(data []byte, dest string, path ...string) error {
  123. reader, err := zip.NewReader(bytes.NewReader(data), int64(len(data)))
  124. if err != nil {
  125. return err
  126. }
  127. return unZipFileWithReader(reader, dest, path...)
  128. }
  129. func unZipFileWithReader(reader *zip.Reader, dest string, path ...string) error {
  130. prefix := ""
  131. if len(path) > 0 {
  132. prefix = gstr.Replace(path[0], `\`, `/`)
  133. }
  134. if err := os.MkdirAll(dest, 0755); err != nil {
  135. return err
  136. }
  137. name := ""
  138. for _, file := range reader.File {
  139. name = gstr.Replace(file.Name, `\`, `/`)
  140. name = gstr.Trim(name, "/")
  141. if prefix != "" {
  142. if name[0:len(prefix)] != prefix {
  143. continue
  144. }
  145. name = name[len(prefix):]
  146. }
  147. path := filepath.Join(dest, name)
  148. if file.FileInfo().IsDir() {
  149. os.MkdirAll(path, file.Mode())
  150. continue
  151. }
  152. dir := filepath.Dir(path)
  153. if len(dir) > 0 {
  154. if _, err := os.Stat(dir); os.IsNotExist(err) {
  155. err = os.MkdirAll(dir, 0755)
  156. if err != nil {
  157. return err
  158. }
  159. }
  160. }
  161. fileReader, err := file.Open()
  162. if err != nil {
  163. return err
  164. }
  165. defer fileReader.Close()
  166. targetFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode())
  167. if err != nil {
  168. return err
  169. }
  170. defer targetFile.Close()
  171. if _, err := io.Copy(targetFile, fileReader); err != nil {
  172. return err
  173. }
  174. }
  175. return nil
  176. }
  177. // zipFile compresses the file of given <path> and writes the content to <zw>.
  178. // The parameter <prefix> indicates the path prefix for zip file.
  179. func zipFile(path string, prefix string, zw *zip.Writer) error {
  180. file, err := os.Open(path)
  181. if err != nil {
  182. return nil
  183. }
  184. defer file.Close()
  185. info, err := file.Stat()
  186. if err != nil {
  187. return err
  188. }
  189. header, err := createFileHeader(info, prefix)
  190. if err != nil {
  191. return err
  192. }
  193. if info.IsDir() {
  194. header.Name += "/"
  195. } else {
  196. header.Method = zip.Deflate
  197. }
  198. writer, err := zw.CreateHeader(header)
  199. if err != nil {
  200. return err
  201. }
  202. if !info.IsDir() {
  203. if _, err = io.Copy(writer, file); err != nil {
  204. return err
  205. }
  206. }
  207. return nil
  208. }
  209. func createFileHeader(info os.FileInfo, prefix string) (*zip.FileHeader, error) {
  210. header, err := zip.FileInfoHeader(info)
  211. if err != nil {
  212. return nil, err
  213. }
  214. if len(prefix) > 0 {
  215. prefix = strings.Replace(prefix, `\`, `/`, -1)
  216. prefix = strings.TrimRight(prefix, `/`)
  217. header.Name = prefix + `/` + header.Name
  218. }
  219. return header, nil
  220. }