github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/Unknwon/cae/zip/zip.go (about) 1 // Copyright 2013 Unknown 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 // not use this file except in compliance with the License. You may obtain 5 // a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 // License for the specific language governing permissions and limitations 13 // under the License. 14 15 // Package zip enables you to transparently read or write ZIP compressed archives and the files inside them. 16 package zip 17 18 import ( 19 "archive/zip" 20 "errors" 21 "io" 22 "os" 23 "path" 24 "path/filepath" 25 "strings" 26 27 "github.com/insionng/yougam/libraries/Unknwon/cae" 28 ) 29 30 // A File represents a file or directory entry in archive. 31 type File struct { 32 *zip.FileHeader 33 oldName string // NOTE: unused, for future change name feature. 34 oldComment string // NOTE: unused, for future change comment feature. 35 absPath string // Absolute path of local file system. 36 tmpPath string 37 } 38 39 // A ZipArchive represents a file archive, compressed with Zip. 40 type ZipArchive struct { 41 *zip.ReadCloser 42 FileName string 43 Comment string 44 NumFiles int 45 Flag int 46 Permission os.FileMode 47 48 files []*File 49 isHasChanged bool 50 51 // For supporting flushing to io.Writer. 52 writer io.Writer 53 isHasWriter bool 54 } 55 56 // OpenFile is the generalized open call; most users will use Open 57 // instead. It opens the named zip file with specified flag 58 // (O_RDONLY etc.) if applicable. If successful, 59 // methods on the returned ZipArchive can be used for I/O. 60 // If there is an error, it will be of type *PathError. 61 func OpenFile(name string, flag int, perm os.FileMode) (*ZipArchive, error) { 62 z := new(ZipArchive) 63 err := z.Open(name, flag, perm) 64 return z, err 65 } 66 67 // Create creates the named zip file, truncating 68 // it if it already exists. If successful, methods on the returned 69 // ZipArchive can be used for I/O; the associated file descriptor has mode 70 // O_RDWR. 71 // If there is an error, it will be of type *PathError. 72 func Create(name string) (*ZipArchive, error) { 73 os.MkdirAll(path.Dir(name), os.ModePerm) 74 return OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666) 75 } 76 77 // Open opens the named zip file for reading. If successful, methods on 78 // the returned ZipArchive can be used for reading; the associated file 79 // descriptor has mode O_RDONLY. 80 // If there is an error, it will be of type *PathError. 81 func Open(name string) (*ZipArchive, error) { 82 return OpenFile(name, os.O_RDONLY, 0) 83 } 84 85 // New accepts a variable that implemented interface io.Writer 86 // for write-only purpose operations. 87 func New(w io.Writer) *ZipArchive { 88 return &ZipArchive{ 89 writer: w, 90 isHasWriter: true, 91 } 92 } 93 94 // List returns a string slice of files' name in ZipArchive. 95 // Specify prefixes will be used as filters. 96 func (z *ZipArchive) List(prefixes ...string) []string { 97 isHasPrefix := len(prefixes) > 0 98 names := make([]string, 0, z.NumFiles) 99 for _, f := range z.files { 100 if isHasPrefix && !cae.HasPrefix(f.Name, prefixes) { 101 continue 102 } 103 names = append(names, f.Name) 104 } 105 return names 106 } 107 108 // AddEmptyDir adds a raw directory entry to ZipArchive, 109 // it returns false if same directory enry already existed. 110 func (z *ZipArchive) AddEmptyDir(dirPath string) bool { 111 if !strings.HasSuffix(dirPath, "/") { 112 dirPath += "/" 113 } 114 115 for _, f := range z.files { 116 if dirPath == f.Name { 117 return false 118 } 119 } 120 121 dirPath = strings.TrimSuffix(dirPath, "/") 122 if strings.Contains(dirPath, "/") { 123 // Auto add all upper level directories. 124 z.AddEmptyDir(path.Dir(dirPath)) 125 } 126 z.files = append(z.files, &File{ 127 FileHeader: &zip.FileHeader{ 128 Name: dirPath + "/", 129 UncompressedSize: 0, 130 }, 131 }) 132 z.updateStat() 133 return true 134 } 135 136 // AddDir adds a directory and subdirectories entries to ZipArchive. 137 func (z *ZipArchive) AddDir(dirPath, absPath string) error { 138 dir, err := os.Open(absPath) 139 if err != nil { 140 return err 141 } 142 defer dir.Close() 143 144 // Make sure we have all upper level directories. 145 z.AddEmptyDir(dirPath) 146 147 fis, err := dir.Readdir(0) 148 if err != nil { 149 return err 150 } 151 for _, fi := range fis { 152 curPath := strings.Replace(absPath+"/"+fi.Name(), "\\", "/", -1) 153 tmpRecPath := strings.Replace(filepath.Join(dirPath, fi.Name()), "\\", "/", -1) 154 if fi.IsDir() { 155 if err = z.AddDir(tmpRecPath, curPath); err != nil { 156 return err 157 } 158 } else { 159 if err = z.AddFile(tmpRecPath, curPath); err != nil { 160 return err 161 } 162 } 163 } 164 return nil 165 } 166 167 // updateStat should be called after every change for rebuilding statistic. 168 func (z *ZipArchive) updateStat() { 169 z.NumFiles = len(z.files) 170 z.isHasChanged = true 171 } 172 173 // AddFile adds a file entry to ZipArchive. 174 func (z *ZipArchive) AddFile(fileName, absPath string) error { 175 if cae.IsFilter(absPath) { 176 return nil 177 } 178 179 f, err := os.Open(absPath) 180 if err != nil { 181 return err 182 } 183 defer f.Close() 184 185 fi, err := f.Stat() 186 if err != nil { 187 return err 188 } 189 190 file := new(File) 191 file.FileHeader, err = zip.FileInfoHeader(fi) 192 if err != nil { 193 return err 194 } 195 file.Name = fileName 196 file.absPath = absPath 197 198 z.AddEmptyDir(path.Dir(fileName)) 199 200 isExist := false 201 for _, f := range z.files { 202 if fileName == f.Name { 203 f = file 204 isExist = true 205 break 206 } 207 } 208 if !isExist { 209 z.files = append(z.files, file) 210 } 211 212 z.updateStat() 213 return nil 214 } 215 216 // DeleteIndex deletes an entry in the archive by its index. 217 func (z *ZipArchive) DeleteIndex(idx int) error { 218 if idx >= z.NumFiles { 219 return errors.New("index out of range of number of files") 220 } 221 222 z.files = append(z.files[:idx], z.files[idx+1:]...) 223 return nil 224 } 225 226 // DeleteName deletes an entry in the archive by its name. 227 func (z *ZipArchive) DeleteName(name string) error { 228 for i, f := range z.files { 229 if f.Name == name { 230 return z.DeleteIndex(i) 231 } 232 } 233 return errors.New("entry with given name not found") 234 }