gcompress_zip.go 6.1 KB

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