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