fileOpr.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821
  1. package filesystem
  2. /*
  3. File Operation Wrapper
  4. author: tobychui
  5. This is a module seperated from the aroz online file system script
  6. that allows cleaner code in the main logic handler of the aroz online system.
  7. WARNING! ALL FILE OPERATION USING THIS WRAPPER SHOULD PASS IN REALPATH
  8. DO NOT USE VIRTUAL PATH FOR ANY OPERATIONS WITH THIS WRAPPER
  9. */
  10. import (
  11. "archive/tar"
  12. "archive/zip"
  13. "compress/flate"
  14. "compress/gzip"
  15. "errors"
  16. "fmt"
  17. "io"
  18. "log"
  19. "os"
  20. "path/filepath"
  21. "strconv"
  22. "strings"
  23. "time"
  24. "imuslab.com/arozos/mod/filesystem/hidden"
  25. archiver "github.com/mholt/archiver/v3"
  26. )
  27. //A basic file zipping function
  28. func ZipFile(filelist []string, outputfile string, includeTopLevelFolder bool) error {
  29. z := archiver.Zip{
  30. CompressionLevel: flate.DefaultCompression,
  31. MkdirAll: true,
  32. SelectiveCompression: true,
  33. OverwriteExisting: false,
  34. ImplicitTopLevelFolder: includeTopLevelFolder,
  35. }
  36. err := z.Archive(filelist, outputfile)
  37. return err
  38. }
  39. //A basic file unzip function
  40. func Unzip(source, destination string) error {
  41. archive, err := zip.OpenReader(source)
  42. if err != nil {
  43. return err
  44. }
  45. defer archive.Close()
  46. for _, file := range archive.Reader.File {
  47. reader, err := file.Open()
  48. if err != nil {
  49. return err
  50. }
  51. defer reader.Close()
  52. path := filepath.Join(destination, file.Name)
  53. err = os.MkdirAll(path, os.ModePerm)
  54. if err != nil {
  55. return err
  56. }
  57. if file.FileInfo().IsDir() {
  58. continue
  59. }
  60. err = os.Remove(path)
  61. if err != nil {
  62. return err
  63. }
  64. writer, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode())
  65. if err != nil {
  66. return err
  67. }
  68. defer writer.Close()
  69. _, err = io.Copy(writer, reader)
  70. if err != nil {
  71. return err
  72. }
  73. }
  74. return nil
  75. }
  76. //Aroz Unzip File with progress update function (current filename / current file count / total file count / progress in percentage)
  77. func ArozUnzipFileWithProgress(filelist []string, outputfile string, progressHandler func(string, int, int, float64)) error {
  78. //Gether the total number of files in all zip files
  79. totalFileCounts := 0
  80. unzippedFileCount := 0
  81. for _, srcFile := range filelist {
  82. archive, err := zip.OpenReader(srcFile)
  83. if err != nil {
  84. return err
  85. }
  86. totalFileCounts += len(archive.Reader.File)
  87. archive.Close()
  88. }
  89. //Start extracting
  90. for _, srcFile := range filelist {
  91. archive, err := zip.OpenReader(srcFile)
  92. if err != nil {
  93. return err
  94. }
  95. defer archive.Close()
  96. for _, file := range archive.Reader.File {
  97. reader, err := file.Open()
  98. if err != nil {
  99. return err
  100. }
  101. defer reader.Close()
  102. path := filepath.Join(outputfile, file.Name)
  103. err = os.MkdirAll(path, os.ModePerm)
  104. if err != nil {
  105. return err
  106. }
  107. if file.FileInfo().IsDir() {
  108. //Folder extracted
  109. //Update the progress
  110. unzippedFileCount++
  111. progressHandler(file.Name, unzippedFileCount, totalFileCounts, float64(unzippedFileCount)/float64(totalFileCounts)*100.0)
  112. continue
  113. }
  114. err = os.Remove(path)
  115. if err != nil {
  116. return err
  117. }
  118. writer, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode())
  119. if err != nil {
  120. return err
  121. }
  122. defer writer.Close()
  123. _, err = io.Copy(writer, reader)
  124. if err != nil {
  125. return err
  126. }
  127. //Update the progress
  128. unzippedFileCount++
  129. progressHandler(file.Name, unzippedFileCount, totalFileCounts, float64(unzippedFileCount)/float64(totalFileCounts)*100.0)
  130. }
  131. }
  132. return nil
  133. }
  134. //Aroz Zip File with progress update function (current filename / current file count / total file count / progress in percentage)
  135. func ArozZipFileWithProgress(filelist []string, outputfile string, includeTopLevelFolder bool, progressHandler func(string, int, int, float64)) error {
  136. //Get the file count from the filelist
  137. totalFileCount := 0
  138. for _, srcpath := range filelist {
  139. if IsDir(srcpath) {
  140. filepath.Walk(srcpath, func(_ string, info os.FileInfo, _ error) error {
  141. if !info.IsDir() {
  142. totalFileCount++
  143. }
  144. return nil
  145. })
  146. } else {
  147. totalFileCount++
  148. }
  149. }
  150. //Create the target zip file
  151. file, err := os.Create(outputfile)
  152. if err != nil {
  153. panic(err)
  154. }
  155. defer file.Close()
  156. writer := zip.NewWriter(file)
  157. defer writer.Close()
  158. currentFileCount := 0
  159. for _, srcpath := range filelist {
  160. if IsDir(srcpath) {
  161. //This is a directory
  162. topLevelFolderName := filepath.ToSlash(filepath.Base(filepath.Dir(srcpath)) + "/" + filepath.Base(srcpath))
  163. err = filepath.Walk(srcpath, func(path string, info os.FileInfo, err error) error {
  164. if err != nil {
  165. return err
  166. }
  167. if info.IsDir() {
  168. return nil
  169. }
  170. if insideHiddenFolder(path) == true {
  171. //This is hidden file / folder. Skip this
  172. return nil
  173. }
  174. file, err := os.Open(path)
  175. if err != nil {
  176. return err
  177. }
  178. defer file.Close()
  179. relativePath := strings.ReplaceAll(filepath.ToSlash(path), filepath.ToSlash(filepath.Clean(srcpath))+"/", "")
  180. if includeTopLevelFolder {
  181. relativePath = topLevelFolderName + "/" + relativePath
  182. } else {
  183. relativePath = filepath.Base(srcpath) + "/" + relativePath
  184. }
  185. f, err := writer.Create(relativePath)
  186. if err != nil {
  187. return err
  188. }
  189. _, err = io.Copy(f, file)
  190. if err != nil {
  191. return err
  192. }
  193. //Update the zip progress
  194. currentFileCount++
  195. progressHandler(filepath.Base(srcpath), currentFileCount, totalFileCount, (float64(currentFileCount)/float64(totalFileCount))*float64(100))
  196. return nil
  197. })
  198. if err != nil {
  199. return err
  200. }
  201. } else {
  202. //This is a file
  203. topLevelFolderName := filepath.Base(filepath.Dir(srcpath))
  204. file, err := os.Open(srcpath)
  205. if err != nil {
  206. return err
  207. }
  208. defer file.Close()
  209. relativePath := filepath.Base(srcpath)
  210. if includeTopLevelFolder {
  211. relativePath = topLevelFolderName + "/" + relativePath
  212. }
  213. f, err := writer.Create(relativePath)
  214. if err != nil {
  215. return err
  216. }
  217. _, err = io.Copy(f, file)
  218. if err != nil {
  219. return err
  220. }
  221. //Update the zip progress
  222. currentFileCount++
  223. progressHandler(filepath.Base(srcpath), currentFileCount, totalFileCount, (float64(currentFileCount)/float64(totalFileCount))*float64(100))
  224. }
  225. }
  226. return nil
  227. }
  228. //ArOZ Zip FIle, but with no progress display
  229. func ArozZipFile(filelist []string, outputfile string, includeTopLevelFolder bool) error {
  230. //Create the target zip file
  231. file, err := os.Create(outputfile)
  232. if err != nil {
  233. return err
  234. }
  235. defer file.Close()
  236. writer := zip.NewWriter(file)
  237. defer writer.Close()
  238. for _, srcpath := range filelist {
  239. if IsDir(srcpath) {
  240. //This is a directory
  241. topLevelFolderName := filepath.ToSlash(filepath.Base(filepath.Dir(srcpath)) + "/" + filepath.Base(srcpath))
  242. err = filepath.Walk(srcpath, func(path string, info os.FileInfo, err error) error {
  243. if err != nil {
  244. return err
  245. }
  246. if info.IsDir() {
  247. return nil
  248. }
  249. if insideHiddenFolder(path) == true {
  250. //This is hidden file / folder. Skip this
  251. return nil
  252. }
  253. file, err := os.Open(path)
  254. if err != nil {
  255. return err
  256. }
  257. defer file.Close()
  258. relativePath := strings.ReplaceAll(filepath.ToSlash(path), filepath.ToSlash(filepath.Clean(srcpath))+"/", "")
  259. if includeTopLevelFolder {
  260. relativePath = topLevelFolderName + "/" + relativePath
  261. } else {
  262. relativePath = filepath.Base(srcpath) + "/" + relativePath
  263. }
  264. f, err := writer.Create(relativePath)
  265. if err != nil {
  266. return err
  267. }
  268. _, err = io.Copy(f, file)
  269. if err != nil {
  270. return err
  271. }
  272. return nil
  273. })
  274. if err != nil {
  275. return err
  276. }
  277. } else {
  278. //This is a file
  279. topLevelFolderName := filepath.Base(filepath.Dir(srcpath))
  280. file, err := os.Open(srcpath)
  281. if err != nil {
  282. return err
  283. }
  284. defer file.Close()
  285. relativePath := filepath.Base(srcpath)
  286. if includeTopLevelFolder {
  287. relativePath = topLevelFolderName + "/" + relativePath
  288. }
  289. f, err := writer.Create(relativePath)
  290. if err != nil {
  291. return err
  292. }
  293. _, err = io.Copy(f, file)
  294. if err != nil {
  295. return err
  296. }
  297. }
  298. }
  299. return nil
  300. }
  301. func insideHiddenFolder(path string) bool {
  302. FileIsHidden, err := hidden.IsHidden(path, true)
  303. if err != nil {
  304. //Read error. Maybe permission issue, assuem is hidden
  305. return true
  306. }
  307. return FileIsHidden
  308. }
  309. func ViewZipFile(filepath string) ([]string, error) {
  310. z := archiver.Zip{}
  311. filelist := []string{}
  312. err := z.Walk(filepath, func(f archiver.File) error {
  313. filelist = append(filelist, f.Name())
  314. return nil
  315. })
  316. return filelist, err
  317. }
  318. func FileCopy(src string, dest string, mode string, progressUpdate func(int, string)) error {
  319. srcRealpath, _ := filepath.Abs(src)
  320. destRealpath, _ := filepath.Abs(dest)
  321. if IsDir(src) && strings.Contains(filepath.ToSlash(destRealpath)+"/", filepath.ToSlash(srcRealpath)+"/") {
  322. //Recursive operation. Reject
  323. return errors.New("Recursive copy operation.")
  324. }
  325. //Check if the copy destination file already have an identical file
  326. copiedFilename := filepath.Base(src)
  327. if fileExists(filepath.Join(dest, filepath.Base(src))) {
  328. if mode == "" {
  329. //Do not specific file exists principle
  330. return errors.New("Destination file already exists.")
  331. } else if mode == "skip" {
  332. //Skip this file
  333. return nil
  334. } else if mode == "overwrite" {
  335. //Continue with the following code
  336. //Check if the copy and paste dest are identical
  337. if filepath.ToSlash(filepath.Clean(src)) == filepath.ToSlash(filepath.Clean(filepath.Join(dest, filepath.Base(src)))) {
  338. //Source and target identical. Cannot overwrite.
  339. return errors.New("Source and destination paths are identical.")
  340. }
  341. } else if mode == "keep" {
  342. //Keep the file but saved with 'Copy' suffix
  343. newFilename := strings.TrimSuffix(filepath.Base(src), filepath.Ext(src)) + " - Copy" + filepath.Ext(src)
  344. //Check if the newFilename already exists. If yes, continue adding suffix
  345. duplicateCounter := 0
  346. for fileExists(filepath.Join(dest, newFilename)) {
  347. duplicateCounter++
  348. newFilename = strings.TrimSuffix(filepath.Base(src), filepath.Ext(src)) + " - Copy(" + strconv.Itoa(duplicateCounter) + ")" + filepath.Ext(src)
  349. if duplicateCounter > 1024 {
  350. //Maxmium loop encountered. For thread safty, terminate here
  351. return errors.New("Too many copies of identical files.")
  352. }
  353. }
  354. copiedFilename = newFilename
  355. } else {
  356. //This exists opr not supported.
  357. return errors.New("Unknown file exists rules given.")
  358. }
  359. }
  360. //Fix the lacking / at the end if true
  361. if dest[len(dest)-1:] != "/" {
  362. dest = dest + "/"
  363. }
  364. //Ready to move. Check if both folder are located in the same root devices. If not, use copy and delete method.
  365. if IsDir(src) {
  366. //Source file is directory. CopyFolder
  367. realDest := filepath.Join(dest, copiedFilename)
  368. //err := dircpy.Copy(src, realDest)
  369. err := dirCopy(src, realDest, progressUpdate)
  370. if err != nil {
  371. return err
  372. }
  373. } else {
  374. //Source is file only. Copy file.
  375. realDest := filepath.Join(dest, copiedFilename)
  376. source, err := os.Open(src)
  377. if err != nil {
  378. return err
  379. }
  380. destination, err := os.Create(realDest)
  381. if err != nil {
  382. return err
  383. }
  384. if progressUpdate != nil {
  385. //Set progress to 100, leave it to upper level abstraction to handle
  386. progressUpdate(100, filepath.Base(realDest))
  387. }
  388. _, err = io.Copy(destination, source)
  389. if err != nil {
  390. return err
  391. }
  392. source.Close()
  393. destination.Close()
  394. }
  395. return nil
  396. }
  397. func FileMove(src string, dest string, mode string, fastMove bool, progressUpdate func(int, string)) error {
  398. srcRealpath, _ := filepath.Abs(src)
  399. destRealpath, _ := filepath.Abs(dest)
  400. if IsDir(src) && strings.Contains(filepath.ToSlash(destRealpath)+"/", filepath.ToSlash(srcRealpath)+"/") {
  401. //Recursive operation. Reject
  402. return errors.New("Recursive move operation.")
  403. }
  404. if !fileExists(dest) {
  405. if fileExists(filepath.Dir(dest)) {
  406. //User pass in the whole path for the folder. Report error usecase.
  407. return errors.New("Dest location should be an existing folder instead of the full path of the moved file.")
  408. }
  409. return errors.New("Dest folder not found")
  410. }
  411. //Fix the lacking / at the end if true
  412. if dest[len(dest)-1:] != "/" {
  413. dest = dest + "/"
  414. }
  415. //Check if the target file already exists.
  416. movedFilename := filepath.Base(src)
  417. if fileExists(filepath.Join(dest, filepath.Base(src))) {
  418. //Handle cases where file already exists
  419. if mode == "" {
  420. //Do not specific file exists principle
  421. return errors.New("Destination file already exists.")
  422. } else if mode == "skip" {
  423. //Skip this file
  424. return nil
  425. } else if mode == "overwrite" {
  426. //Continue with the following code
  427. //Check if the copy and paste dest are identical
  428. if filepath.ToSlash(filepath.Clean(src)) == filepath.ToSlash(filepath.Clean(filepath.Join(dest, filepath.Base(src)))) {
  429. //Source and target identical. Cannot overwrite.
  430. return errors.New("Source and destination paths are identical.")
  431. }
  432. } else if mode == "keep" {
  433. //Keep the file but saved with 'Copy' suffix
  434. newFilename := strings.TrimSuffix(filepath.Base(src), filepath.Ext(src)) + " - Copy" + filepath.Ext(src)
  435. //Check if the newFilename already exists. If yes, continue adding suffix
  436. duplicateCounter := 0
  437. for fileExists(filepath.Join(dest, newFilename)) {
  438. duplicateCounter++
  439. newFilename = strings.TrimSuffix(filepath.Base(src), filepath.Ext(src)) + " - Copy(" + strconv.Itoa(duplicateCounter) + ")" + filepath.Ext(src)
  440. if duplicateCounter > 1024 {
  441. //Maxmium loop encountered. For thread safty, terminate here
  442. return errors.New("Too many copies of identical files.")
  443. }
  444. }
  445. movedFilename = newFilename
  446. } else {
  447. //This exists opr not supported.
  448. return errors.New("Unknown file exists rules given.")
  449. }
  450. }
  451. if fastMove {
  452. //Ready to move with the quick rename method
  453. realDest := dest + movedFilename
  454. err := os.Rename(src, realDest)
  455. if err == nil {
  456. //Fast move success
  457. return nil
  458. }
  459. //Fast move failed. Back to the original copy and move method
  460. }
  461. //Ready to move. Check if both folder are located in the same root devices. If not, use copy and delete method.
  462. if IsDir(src) {
  463. //Source file is directory. CopyFolder
  464. realDest := dest + movedFilename
  465. //err := dircpy.Copy(src, realDest)
  466. err := dirCopy(src, realDest, progressUpdate)
  467. if err != nil {
  468. return err
  469. } else {
  470. //Move completed. Remove source file.
  471. os.RemoveAll(src)
  472. return nil
  473. }
  474. } else {
  475. //Source is file only. Copy file.
  476. realDest := dest + movedFilename
  477. /*
  478. Updates 20-10-2020, replaced io.Copy to BufferedLargeFileCopy
  479. Legacy code removed.
  480. */
  481. //Update the progress
  482. if progressUpdate != nil {
  483. progressUpdate(100, filepath.Base(src))
  484. }
  485. err := BufferedLargeFileCopy(src, realDest, 8192)
  486. if err != nil {
  487. log.Println("BLFC error: ", err.Error())
  488. return err
  489. }
  490. //Delete the source file after copy
  491. err = os.Remove(src)
  492. counter := 0
  493. for err != nil {
  494. //Sometime Windows need this to prevent windows caching bring problems to file remove
  495. time.Sleep(1 * time.Second)
  496. os.Remove(src)
  497. counter++
  498. log.Println("Retrying to remove file: " + src)
  499. if counter > 10 {
  500. return errors.New("Source file remove failed.")
  501. }
  502. }
  503. }
  504. return nil
  505. }
  506. //Copy a given directory, with no progress udpate
  507. func CopyDir(src string, dest string) error {
  508. return dirCopy(src, dest, func(progress int, name string) {})
  509. }
  510. //Replacment of the legacy dirCopy plugin with filepath.Walk function. Allowing real time progress update to front end
  511. func dirCopy(src string, realDest string, progressUpdate func(int, string)) error {
  512. //Get the total file counts
  513. totalFileCounts := int64(0)
  514. filepath.Walk(src, func(path string, info os.FileInfo, err error) error {
  515. if !info.IsDir() {
  516. //Updates 22 April 2021, chnaged from file count to file size for progress update
  517. //totalFileCounts++
  518. totalFileCounts += info.Size()
  519. }
  520. return nil
  521. })
  522. //Make the destinaton directory
  523. if !fileExists(realDest) {
  524. os.Mkdir(realDest, 0755)
  525. }
  526. //Start moving
  527. fileCounter := int64(0)
  528. err := filepath.Walk(src, func(path string, info os.FileInfo, err error) error {
  529. srcAbs, _ := filepath.Abs(src)
  530. pathAbs, _ := filepath.Abs(path)
  531. var folderRootRelative string = strings.Replace(pathAbs, srcAbs, "", 1)
  532. if folderRootRelative == "" {
  533. return nil
  534. }
  535. if info.IsDir() {
  536. //Mkdir base on relative path
  537. return os.MkdirAll(filepath.Join(realDest, folderRootRelative), 0755)
  538. } else {
  539. //fileCounter++
  540. fileCounter += info.Size()
  541. //Move file base on relative path
  542. fileSrc := filepath.ToSlash(filepath.Join(filepath.Clean(src), folderRootRelative))
  543. fileDest := filepath.ToSlash(filepath.Join(filepath.Clean(realDest), folderRootRelative))
  544. //Update move progress
  545. if progressUpdate != nil {
  546. progressUpdate(int(float64(fileCounter)/float64(totalFileCounts)*100), filepath.Base(fileSrc))
  547. }
  548. //Move the file using BLFC
  549. err := BufferedLargeFileCopy(fileSrc, fileDest, 8192)
  550. if err != nil {
  551. //Ignore and continue
  552. log.Println("BLFC Error:", err.Error())
  553. return nil
  554. }
  555. /*
  556. //Move fiel using IO Copy
  557. err := BasicFileCopy(fileSrc, fileDest)
  558. if err != nil {
  559. log.Println("Basic Copy Error: ", err.Error())
  560. return nil
  561. }
  562. */
  563. }
  564. return nil
  565. })
  566. return err
  567. }
  568. func BasicFileCopy(src string, dst string) error {
  569. sourceFileStat, err := os.Stat(src)
  570. if err != nil {
  571. return err
  572. }
  573. if !sourceFileStat.Mode().IsRegular() {
  574. return fmt.Errorf("%s is not a regular file", src)
  575. }
  576. source, err := os.Open(src)
  577. if err != nil {
  578. return err
  579. }
  580. defer source.Close()
  581. destination, err := os.Create(dst)
  582. if err != nil {
  583. return err
  584. }
  585. defer destination.Close()
  586. _, err = io.Copy(destination, source)
  587. return err
  588. }
  589. //Use for copying large file using buffering method. Allowing copying large file with little RAM
  590. func BufferedLargeFileCopy(src string, dst string, BUFFERSIZE int64) error {
  591. sourceFileStat, err := os.Stat(src)
  592. if err != nil {
  593. return err
  594. }
  595. if !sourceFileStat.Mode().IsRegular() {
  596. return errors.New("Invalid file source")
  597. }
  598. source, err := os.Open(src)
  599. if err != nil {
  600. return err
  601. }
  602. destination, err := os.Create(dst)
  603. if err != nil {
  604. return err
  605. }
  606. buf := make([]byte, BUFFERSIZE)
  607. for {
  608. n, err := source.Read(buf)
  609. if err != nil && err != io.EOF {
  610. source.Close()
  611. destination.Close()
  612. return err
  613. }
  614. if n == 0 {
  615. source.Close()
  616. destination.Close()
  617. break
  618. }
  619. if _, err := destination.Write(buf[:n]); err != nil {
  620. source.Close()
  621. destination.Close()
  622. return err
  623. }
  624. }
  625. return nil
  626. }
  627. func IsDir(path string) bool {
  628. if fileExists(path) == false {
  629. return false
  630. }
  631. fi, err := os.Stat(path)
  632. if err != nil {
  633. log.Fatal(err)
  634. return false
  635. }
  636. switch mode := fi.Mode(); {
  637. case mode.IsDir():
  638. return true
  639. case mode.IsRegular():
  640. return false
  641. }
  642. return false
  643. }
  644. //Unzip tar.gz file
  645. func ExtractTarGzipFile(filename string, outfile string) error {
  646. f, err := os.Open(filename)
  647. if err != nil {
  648. return err
  649. }
  650. err = ExtractTarGzipByStream(filepath.Clean(outfile), f, true)
  651. if err != nil {
  652. return err
  653. }
  654. return f.Close()
  655. }
  656. func ExtractTarGzipByStream(basedir string, gzipStream io.Reader, onErrorResumeNext bool) error {
  657. uncompressedStream, err := gzip.NewReader(gzipStream)
  658. if err != nil {
  659. return err
  660. }
  661. tarReader := tar.NewReader(uncompressedStream)
  662. for {
  663. header, err := tarReader.Next()
  664. if err == io.EOF {
  665. break
  666. }
  667. if err != nil {
  668. return err
  669. }
  670. switch header.Typeflag {
  671. case tar.TypeDir:
  672. err := os.Mkdir(header.Name, 0755)
  673. if err != nil {
  674. if !onErrorResumeNext {
  675. return err
  676. }
  677. }
  678. case tar.TypeReg:
  679. outFile, err := os.Create(filepath.Join(basedir, header.Name))
  680. if err != nil {
  681. if !onErrorResumeNext {
  682. return err
  683. }
  684. }
  685. _, err = io.Copy(outFile, tarReader)
  686. if err != nil {
  687. if !onErrorResumeNext {
  688. return err
  689. }
  690. }
  691. outFile.Close()
  692. default:
  693. //Unknown filetype, continue
  694. }
  695. }
  696. return nil
  697. }