compress.go 7.5 KB

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