write.go 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  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. package zip
  15. import (
  16. "archive/zip"
  17. "fmt"
  18. "io"
  19. "os"
  20. "path"
  21. "path/filepath"
  22. "strings"
  23. "github.com/Unknwon/cae"
  24. )
  25. // Switcher of printing trace information when pack and extract.
  26. var Verbose = true
  27. // extractFile extracts zip.File to file system.
  28. func extractFile(f *zip.File, destPath string) error {
  29. filePath := path.Join(destPath, f.Name)
  30. os.MkdirAll(path.Dir(filePath), os.ModePerm)
  31. rc, err := f.Open()
  32. if err != nil {
  33. return err
  34. }
  35. defer rc.Close()
  36. fw, err := os.Create(filePath)
  37. if err != nil {
  38. return err
  39. }
  40. defer fw.Close()
  41. if _, err = io.Copy(fw, rc); err != nil {
  42. return err
  43. }
  44. // Skip symbolic links.
  45. if f.FileInfo().Mode()&os.ModeSymlink != 0 {
  46. return nil
  47. }
  48. // Set back file information.
  49. if err = os.Chtimes(filePath, f.ModTime(), f.ModTime()); err != nil {
  50. return err
  51. }
  52. return os.Chmod(filePath, f.FileInfo().Mode())
  53. }
  54. var defaultExtractFunc = func(fullName string, fi os.FileInfo) error {
  55. if !Verbose {
  56. return nil
  57. }
  58. fmt.Println("Extracting file..." + fullName)
  59. return nil
  60. }
  61. // ExtractToFunc extracts the whole archive or the given files to the
  62. // specified destination.
  63. // It accepts a function as a middleware for custom operations.
  64. func (z *ZipArchive) ExtractToFunc(destPath string, fn cae.HookFunc, entries ...string) (err error) {
  65. destPath = strings.Replace(destPath, "\\", "/", -1)
  66. isHasEntry := len(entries) > 0
  67. if Verbose {
  68. fmt.Println("Unzipping " + z.FileName + "...")
  69. }
  70. os.MkdirAll(destPath, os.ModePerm)
  71. for _, f := range z.File {
  72. f.Name = strings.Replace(f.Name, "\\", "/", -1)
  73. // Directory.
  74. if strings.HasSuffix(f.Name, "/") {
  75. if isHasEntry {
  76. if cae.IsEntry(f.Name, entries) {
  77. if err = fn(f.Name, f.FileInfo()); err != nil {
  78. continue
  79. }
  80. os.MkdirAll(path.Join(destPath, f.Name), os.ModePerm)
  81. }
  82. continue
  83. }
  84. if err = fn(f.Name, f.FileInfo()); err != nil {
  85. continue
  86. }
  87. os.MkdirAll(path.Join(destPath, f.Name), os.ModePerm)
  88. continue
  89. }
  90. // File.
  91. if isHasEntry {
  92. if cae.IsEntry(f.Name, entries) {
  93. if err = fn(f.Name, f.FileInfo()); err != nil {
  94. continue
  95. }
  96. err = extractFile(f, destPath)
  97. }
  98. } else {
  99. if err = fn(f.Name, f.FileInfo()); err != nil {
  100. continue
  101. }
  102. err = extractFile(f, destPath)
  103. }
  104. if err != nil {
  105. return err
  106. }
  107. }
  108. return nil
  109. }
  110. // ExtractToFunc extracts the whole archive or the given files to the
  111. // specified destination.
  112. // It accepts a function as a middleware for custom operations.
  113. func ExtractToFunc(srcPath, destPath string, fn cae.HookFunc, entries ...string) (err error) {
  114. z, err := Open(srcPath)
  115. if err != nil {
  116. return err
  117. }
  118. defer z.Close()
  119. return z.ExtractToFunc(destPath, fn, entries...)
  120. }
  121. // ExtractTo extracts the whole archive or the given files to the
  122. // specified destination.
  123. // Call Flush() to apply changes before this.
  124. func (z *ZipArchive) ExtractTo(destPath string, entries ...string) (err error) {
  125. return z.ExtractToFunc(destPath, defaultExtractFunc, entries...)
  126. }
  127. // ExtractTo extracts given archive or the given files to the
  128. // specified destination.
  129. func ExtractTo(srcPath, destPath string, entries ...string) (err error) {
  130. return ExtractToFunc(srcPath, destPath, defaultExtractFunc, entries...)
  131. }
  132. // extractFile extracts file from ZipArchive to file system.
  133. func (z *ZipArchive) extractFile(f *File) error {
  134. if !z.isHasWriter {
  135. for _, zf := range z.ReadCloser.File {
  136. if f.Name == zf.Name {
  137. return extractFile(zf, path.Dir(f.tmpPath))
  138. }
  139. }
  140. }
  141. return cae.Copy(f.tmpPath, f.absPath)
  142. }
  143. // Flush saves changes to original zip file if any.
  144. func (z *ZipArchive) Flush() error {
  145. if !z.isHasChanged || (z.ReadCloser == nil && !z.isHasWriter) {
  146. return nil
  147. }
  148. // Extract to tmp path and pack back.
  149. tmpPath := path.Join(os.TempDir(), "cae", path.Base(z.FileName))
  150. os.RemoveAll(tmpPath)
  151. defer os.RemoveAll(tmpPath)
  152. for _, f := range z.files {
  153. if strings.HasSuffix(f.Name, "/") {
  154. os.MkdirAll(path.Join(tmpPath, f.Name), os.ModePerm)
  155. continue
  156. }
  157. // Relative path inside zip temporary changed.
  158. f.tmpPath = path.Join(tmpPath, f.Name)
  159. if err := z.extractFile(f); err != nil {
  160. return err
  161. }
  162. }
  163. if z.isHasWriter {
  164. return packToWriter(tmpPath, z.writer, defaultPackFunc, true)
  165. }
  166. if err := PackTo(tmpPath, z.FileName); err != nil {
  167. return err
  168. }
  169. return z.Open(z.FileName, os.O_RDWR|os.O_TRUNC, z.Permission)
  170. }
  171. // packFile packs a file or directory to zip.Writer.
  172. func packFile(srcFile string, recPath string, zw *zip.Writer, fi os.FileInfo) error {
  173. if fi.IsDir() {
  174. fh, err := zip.FileInfoHeader(fi)
  175. if err != nil {
  176. return err
  177. }
  178. fh.Name = recPath + "/"
  179. if _, err = zw.CreateHeader(fh); err != nil {
  180. return err
  181. }
  182. } else {
  183. fh, err := zip.FileInfoHeader(fi)
  184. if err != nil {
  185. return err
  186. }
  187. fh.Name = recPath
  188. fh.Method = zip.Deflate
  189. fw, err := zw.CreateHeader(fh)
  190. if err != nil {
  191. return err
  192. }
  193. if fi.Mode()&os.ModeSymlink != 0 {
  194. target, err := os.Readlink(srcFile)
  195. if err != nil {
  196. return err
  197. }
  198. if _, err = fw.Write([]byte(target)); err != nil {
  199. return err
  200. }
  201. } else {
  202. f, err := os.Open(srcFile)
  203. if err != nil {
  204. return err
  205. }
  206. defer f.Close()
  207. if _, err = io.Copy(fw, f); err != nil {
  208. return err
  209. }
  210. }
  211. }
  212. return nil
  213. }
  214. // packDir packs a directory and its subdirectories and files
  215. // recursively to zip.Writer.
  216. func packDir(srcPath string, recPath string, zw *zip.Writer, fn cae.HookFunc) error {
  217. dir, err := os.Open(srcPath)
  218. if err != nil {
  219. return err
  220. }
  221. defer dir.Close()
  222. fis, err := dir.Readdir(0)
  223. if err != nil {
  224. return err
  225. }
  226. for _, fi := range fis {
  227. if cae.IsFilter(fi.Name()) {
  228. continue
  229. }
  230. curPath := srcPath + "/" + fi.Name()
  231. tmpRecPath := filepath.Join(recPath, fi.Name())
  232. if err = fn(curPath, fi); err != nil {
  233. continue
  234. }
  235. if fi.IsDir() {
  236. if err = packFile(srcPath, tmpRecPath, zw, fi); err != nil {
  237. return err
  238. }
  239. err = packDir(curPath, tmpRecPath, zw, fn)
  240. } else {
  241. err = packFile(curPath, tmpRecPath, zw, fi)
  242. }
  243. if err != nil {
  244. return err
  245. }
  246. }
  247. return nil
  248. }
  249. // packToWriter packs given path object to io.Writer.
  250. func packToWriter(srcPath string, w io.Writer, fn func(fullName string, fi os.FileInfo) error, includeDir bool) error {
  251. zw := zip.NewWriter(w)
  252. defer zw.Close()
  253. f, err := os.Open(srcPath)
  254. if err != nil {
  255. return err
  256. }
  257. defer f.Close()
  258. fi, err := f.Stat()
  259. if err != nil {
  260. return err
  261. }
  262. basePath := path.Base(srcPath)
  263. if fi.IsDir() {
  264. if includeDir {
  265. if err = packFile(srcPath, basePath, zw, fi); err != nil {
  266. return err
  267. }
  268. } else {
  269. basePath = ""
  270. }
  271. return packDir(srcPath, basePath, zw, fn)
  272. }
  273. return packFile(srcPath, basePath, zw, fi)
  274. }
  275. // packTo packs given source path object to target path.
  276. func packTo(srcPath, destPath string, fn cae.HookFunc, includeDir bool) error {
  277. fw, err := os.Create(destPath)
  278. if err != nil {
  279. return err
  280. }
  281. defer fw.Close()
  282. return packToWriter(srcPath, fw, fn, includeDir)
  283. }
  284. // PackToFunc packs the complete archive to the specified destination.
  285. // It accepts a function as a middleware for custom operations.
  286. func PackToFunc(srcPath, destPath string, fn func(fullName string, fi os.FileInfo) error, includeDir ...bool) error {
  287. isIncludeDir := false
  288. if len(includeDir) > 0 && includeDir[0] {
  289. isIncludeDir = true
  290. }
  291. return packTo(srcPath, destPath, fn, isIncludeDir)
  292. }
  293. var defaultPackFunc = func(fullName string, fi os.FileInfo) error {
  294. if !Verbose {
  295. return nil
  296. }
  297. if fi.IsDir() {
  298. fmt.Printf("Adding dir...%s\n", fullName)
  299. } else {
  300. fmt.Printf("Adding file...%s\n", fullName)
  301. }
  302. return nil
  303. }
  304. // PackTo packs the whole archive to the specified destination.
  305. // Call Flush() will automatically call this in the end.
  306. func PackTo(srcPath, destPath string, includeDir ...bool) error {
  307. return PackToFunc(srcPath, destPath, defaultPackFunc, includeDir...)
  308. }
  309. // Close opens or creates archive and save changes.
  310. func (z *ZipArchive) Close() (err error) {
  311. if err = z.Flush(); err != nil {
  312. return err
  313. }
  314. if z.ReadCloser != nil {
  315. if err = z.ReadCloser.Close(); err != nil {
  316. return err
  317. }
  318. z.ReadCloser = nil
  319. }
  320. return nil
  321. }