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