compress.go 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. package files
  2. import (
  3. "archive/tar"
  4. "archive/zip"
  5. "bufio"
  6. "bytes"
  7. "compress/gzip"
  8. "fmt"
  9. "io"
  10. "os"
  11. "path/filepath"
  12. "go.uber.org/zap"
  13. )
  14. // ========== ZIP ==========
  15. // 压缩文件或文件夹为 zip
  16. func Zip(srcPath, destZip string) error {
  17. zipFile, err := os.Create(destZip)
  18. if err != nil {
  19. return err
  20. }
  21. defer zipFile.Close()
  22. zw := zip.NewWriter(zipFile)
  23. defer zw.Close()
  24. info, err := os.Stat(srcPath)
  25. if err != nil {
  26. return err
  27. }
  28. if info.IsDir() {
  29. // 压缩目录
  30. return filepath.Walk(srcPath, func(path string, info os.FileInfo, err error) error {
  31. if err != nil {
  32. return err
  33. }
  34. relPath, err := filepath.Rel(srcPath, path)
  35. if err != nil {
  36. return err
  37. }
  38. if relPath == "." {
  39. return nil
  40. }
  41. if info.IsDir() {
  42. _, err := zw.Create(relPath + "/")
  43. return err
  44. }
  45. // 添加文件
  46. w, err := zw.Create(relPath)
  47. if err != nil {
  48. return err
  49. }
  50. f, err := os.Open(path)
  51. if err != nil {
  52. return err
  53. }
  54. defer f.Close()
  55. _, err = io.Copy(w, f)
  56. return err
  57. })
  58. } else {
  59. // 压缩单文件
  60. w, err := zw.Create(info.Name())
  61. if err != nil {
  62. return err
  63. }
  64. f, err := os.Open(srcPath)
  65. if err != nil {
  66. return err
  67. }
  68. defer f.Close()
  69. _, err = io.Copy(w, f)
  70. return err
  71. }
  72. }
  73. // 解压 zip
  74. func Unzip(src, dest string) error {
  75. r, err := zip.OpenReader(src)
  76. if err != nil {
  77. return err
  78. }
  79. defer r.Close()
  80. for _, f := range r.File {
  81. // 拼接目标路径
  82. fpath := filepath.Join(dest, f.Name)
  83. // 目录则创建
  84. if f.FileInfo().IsDir() {
  85. if err := os.MkdirAll(fpath, os.ModePerm); err != nil {
  86. return err
  87. }
  88. continue
  89. }
  90. // 创建父目录
  91. if err := os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err != nil {
  92. return err
  93. }
  94. // 解压文件
  95. rc, err := f.Open()
  96. if err != nil {
  97. return err
  98. }
  99. defer rc.Close()
  100. out, err := os.Create(fpath)
  101. if err != nil {
  102. return err
  103. }
  104. defer out.Close()
  105. if _, err := io.Copy(out, rc); err != nil {
  106. return err
  107. }
  108. }
  109. return nil
  110. }
  111. // ========== TAR ==========
  112. // 打包文件或目录为 tar
  113. func Tar(srcPath, destTar string) error {
  114. tarFile, err := os.Create(destTar)
  115. if err != nil {
  116. return err
  117. }
  118. defer tarFile.Close()
  119. tw := tar.NewWriter(tarFile)
  120. defer tw.Close()
  121. info, err := os.Stat(srcPath)
  122. if err != nil {
  123. return err
  124. }
  125. if info.IsDir() {
  126. // 打包目录
  127. return filepath.Walk(srcPath, func(path string, info os.FileInfo, err error) error {
  128. if err != nil {
  129. return err
  130. }
  131. relPath, err := filepath.Rel(srcPath, path)
  132. if err != nil {
  133. return err
  134. }
  135. if relPath == "." {
  136. return nil
  137. }
  138. header, err := tar.FileInfoHeader(info, "")
  139. if err != nil {
  140. return err
  141. }
  142. header.Name = relPath
  143. if err := tw.WriteHeader(header); err != nil {
  144. return err
  145. }
  146. if !info.Mode().IsRegular() {
  147. return nil
  148. }
  149. f, err := os.Open(path)
  150. if err != nil {
  151. return err
  152. }
  153. defer f.Close()
  154. _, err = io.Copy(tw, f)
  155. return err
  156. })
  157. } else {
  158. // 打包单文件
  159. header, err := tar.FileInfoHeader(info, "")
  160. if err != nil {
  161. return err
  162. }
  163. header.Name = info.Name()
  164. if err := tw.WriteHeader(header); err != nil {
  165. return err
  166. }
  167. if !info.Mode().IsRegular() {
  168. return nil
  169. }
  170. f, err := os.Open(srcPath)
  171. if err != nil {
  172. return err
  173. }
  174. defer f.Close()
  175. _, err = io.Copy(tw, f)
  176. return err
  177. }
  178. }
  179. // 解包 tar
  180. func Untar(srcTar, destDir string) error {
  181. f, err := os.Open(srcTar)
  182. if err != nil {
  183. return err
  184. }
  185. defer f.Close()
  186. tr := tar.NewReader(f)
  187. for {
  188. header, err := tr.Next()
  189. if err == io.EOF {
  190. break
  191. }
  192. if err != nil {
  193. return err
  194. }
  195. fpath := filepath.Join(destDir, header.Name)
  196. switch header.Typeflag {
  197. case tar.TypeDir:
  198. if err := os.MkdirAll(fpath, os.ModePerm); err != nil {
  199. return err
  200. }
  201. case tar.TypeReg:
  202. if err := os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err != nil {
  203. return err
  204. }
  205. out, err := os.Create(fpath)
  206. if err != nil {
  207. return err
  208. }
  209. defer out.Close()
  210. if _, err := io.Copy(out, tr); err != nil {
  211. return err
  212. }
  213. }
  214. }
  215. return nil
  216. }
  217. // ========== GZIP ==========
  218. // 压缩单文件为 .gz
  219. func Gzip(srcFile, destGz string) error {
  220. in, err := os.Open(srcFile)
  221. if err != nil {
  222. return err
  223. }
  224. defer in.Close()
  225. out, err := os.Create(destGz)
  226. if err != nil {
  227. return err
  228. }
  229. defer out.Close()
  230. gw := gzip.NewWriter(out)
  231. defer gw.Close()
  232. _, err = io.Copy(gw, in)
  233. return err
  234. }
  235. // 解压 .gz 为文件
  236. func Gunzip(srcGz, destFile string) error {
  237. in, err := os.Open(srcGz)
  238. if err != nil {
  239. return err
  240. }
  241. defer in.Close()
  242. gr, err := gzip.NewReader(in)
  243. if err != nil {
  244. return err
  245. }
  246. defer gr.Close()
  247. out, err := os.Create(destFile)
  248. if err != nil {
  249. return err
  250. }
  251. defer out.Close()
  252. _, err = io.Copy(out, gr)
  253. return err
  254. }
  255. // ========== TGZ ==========
  256. // 压缩文件或文件夹为 tgz
  257. func TarGz(srcPath, destTgz string) error {
  258. tempFile, err := os.CreateTemp("", "fwhtb-temp-*.tar")
  259. if err != nil {
  260. return err
  261. }
  262. defer os.Remove(tempFile.Name()) // 程序结束时删除
  263. // 打包目录为tar文件
  264. if err = Tar(srcPath, tempFile.Name()); err != nil {
  265. return err
  266. }
  267. // 压缩单tar文件为.tgz或.tar.gz
  268. if err = Gzip(tempFile.Name(), destTgz); err != nil {
  269. return err
  270. }
  271. return nil
  272. }
  273. // 解压 tgz
  274. func UntarGz(srcTgz, destDir string) error {
  275. tempFile, err := os.CreateTemp("", "fwhtb-temp-*.tar")
  276. if err != nil {
  277. return err
  278. }
  279. defer os.Remove(tempFile.Name()) // 程序结束时删除
  280. // 解压.tgz或.tar.gz为tar文件
  281. if err = Gunzip(srcTgz, tempFile.Name()); err != nil {
  282. return err
  283. }
  284. // 解包tar文件
  285. if err = Untar(tempFile.Name(), destDir); err != nil {
  286. return err
  287. }
  288. return nil
  289. }
  290. // ========== 自动解包 ==========
  291. // 自动解包
  292. func AutoExtract(srcFile, destDir string) error {
  293. ext := filepath.Ext(srcFile)
  294. name := filepath.Base(srcFile)
  295. switch {
  296. // .tar.gz / .tgz
  297. case ext == ".gz" && (filepath.Ext(name[:len(name)-len(ext)]) == ".tar"):
  298. return UntarGz(srcFile, destDir)
  299. case ext == ".tgz":
  300. return UntarGz(srcFile, destDir)
  301. // .zip
  302. case ext == ".zip":
  303. return Unzip(srcFile, destDir)
  304. // .tar
  305. case ext == ".tar":
  306. return Untar(srcFile, destDir)
  307. // .gz (单文件压缩)
  308. case ext == ".gz":
  309. destFile := filepath.Join(destDir, name[:len(name)-3]) // 去掉.gz
  310. return Gunzip(srcFile, destFile)
  311. default:
  312. // 扩展名不明,尝试魔数检测
  313. zap.L().Info("未知扩展名,尝试魔数检测")
  314. return detectByMagic(srcFile, destDir)
  315. }
  316. }
  317. // 魔数检测
  318. func detectByMagic(srcFile, destDir string) error {
  319. f, err := os.Open(srcFile)
  320. if err != nil {
  321. return err
  322. }
  323. defer f.Close()
  324. reader := bufio.NewReader(f)
  325. head := make([]byte, 6)
  326. _, err = reader.Read(head)
  327. if err != nil {
  328. return err
  329. }
  330. switch {
  331. case bytes.HasPrefix(head, []byte{0x50, 0x4B, 0x03, 0x04}): // ZIP
  332. return Unzip(srcFile, destDir)
  333. case bytes.HasPrefix(head, []byte{0x1F, 0x8B}): // GZIP
  334. // 可能是 .gz 或 .tar.gz,尝试当 gzip 解开再看
  335. tempFile := srcFile + ".tmp"
  336. if err := Gunzip(srcFile, tempFile); err != nil {
  337. return err
  338. }
  339. // 检查是不是 tar
  340. tf, _ := os.Open(tempFile)
  341. defer tf.Close()
  342. buf := make([]byte, 262)
  343. tf.Read(buf)
  344. if bytes.Contains(buf[257:262], []byte("ustar")) {
  345. return UntarGz(srcFile, destDir) // 是 tar.gz
  346. }
  347. return Gunzip(srcFile, filepath.Join(destDir, filepath.Base(srcFile[:len(srcFile)-3])))
  348. }
  349. return fmt.Errorf("无法识别文件类型: %s", srcFile)
  350. }