fileOpr.go 20 KB

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