fileOpr.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807
  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(srcFsh *FileSystemHandler, src string, destFsh *FileSystemHandler, dest string, mode string, progressUpdate func(int, string)) error {
  326. srcFshAbs := srcFsh.FileSystemAbstraction
  327. destFshAbs := destFsh.FileSystemAbstraction
  328. if srcFshAbs.IsDir(src) && strings.HasPrefix(dest, src) {
  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 destFshAbs.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 destFshAbs.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. //Ready to move. Check if both folder are located in the same root devices. If not, use copy and delete method.
  368. if srcFshAbs.IsDir(src) {
  369. //Source file is directory. CopyFolder
  370. realDest := filepath.Join(dest, copiedFilename)
  371. err := dirCopy(srcFsh, src, destFsh, realDest, progressUpdate)
  372. if err != nil {
  373. return err
  374. }
  375. } else {
  376. //Source is file only. Copy file.
  377. realDest := filepath.Join(dest, copiedFilename)
  378. f, err := srcFshAbs.ReadStream(src)
  379. if err != nil {
  380. return err
  381. }
  382. err = destFshAbs.WriteStream(realDest, f, 0775)
  383. if err != nil {
  384. return err
  385. }
  386. if progressUpdate != nil {
  387. //Set progress to 100, leave it to upper level abstraction to handle
  388. progressUpdate(100, filepath.Base(realDest))
  389. }
  390. }
  391. return nil
  392. }
  393. func FileMove(srcFsh *FileSystemHandler, src string, destFsh *FileSystemHandler, dest string, mode string, fastMove bool, progressUpdate func(int, string)) error {
  394. srcAbst := srcFsh.FileSystemAbstraction
  395. destAbst := destFsh.FileSystemAbstraction
  396. src = filepath.ToSlash(src)
  397. dest = filepath.ToSlash(dest)
  398. if srcAbst.IsDir(src) && strings.HasPrefix(dest, src) {
  399. //Recursive operation. Reject
  400. return errors.New("Recursive move operation.")
  401. }
  402. if !destAbst.FileExists(dest) {
  403. if destAbst.FileExists(filepath.Dir(dest)) {
  404. //User pass in the whole path for the folder. Report error usecase.
  405. return errors.New("Dest location should be an existing folder instead of the full path of the moved file.")
  406. }
  407. os.MkdirAll(dest, 0775)
  408. }
  409. //Check if the target file already exists.
  410. movedFilename := filepath.Base(src)
  411. if destAbst.FileExists(filepath.Join(dest, filepath.Base(src))) {
  412. //Handle cases where file already exists
  413. if mode == "" {
  414. //Do not specific file exists principle
  415. return errors.New("Destination file already exists.")
  416. } else if mode == "skip" {
  417. //Skip this file
  418. return nil
  419. } else if mode == "overwrite" {
  420. //Continue with the following code
  421. //Check if the copy and paste dest are identical
  422. if filepath.ToSlash(filepath.Clean(src)) == filepath.ToSlash(filepath.Clean(filepath.Join(dest, filepath.Base(src)))) {
  423. //Source and target identical. Cannot overwrite.
  424. return errors.New("Source and destination paths are identical.")
  425. }
  426. } else if mode == "keep" {
  427. //Keep the file but saved with 'Copy' suffix
  428. newFilename := strings.TrimSuffix(filepath.Base(src), filepath.Ext(src)) + " - Copy" + filepath.Ext(src)
  429. //Check if the newFilename already exists. If yes, continue adding suffix
  430. duplicateCounter := 0
  431. for destAbst.FileExists(filepath.Join(dest, newFilename)) {
  432. duplicateCounter++
  433. newFilename = strings.TrimSuffix(filepath.Base(src), filepath.Ext(src)) + " - Copy(" + strconv.Itoa(duplicateCounter) + ")" + filepath.Ext(src)
  434. if duplicateCounter > 1024 {
  435. //Maxmium loop encountered. For thread safty, terminate here
  436. return errors.New("Too many copies of identical files.")
  437. }
  438. }
  439. movedFilename = newFilename
  440. } else {
  441. //This exists opr not supported.
  442. return errors.New("Unknown file exists rules given.")
  443. }
  444. }
  445. if fastMove {
  446. //Ready to move with the quick rename method
  447. realDest := filepath.Join(dest, movedFilename)
  448. err := os.Rename(src, realDest)
  449. if err == nil {
  450. //Fast move success
  451. return nil
  452. }
  453. //Fast move failed. Back to the original copy and move method
  454. }
  455. //Ready to move. Check if both folder are located in the same root devices. If not, use copy and delete method.
  456. if srcAbst.IsDir(src) {
  457. //Source file is directory. CopyFolder
  458. realDest := filepath.Join(dest, movedFilename)
  459. //err := dircpy.Copy(src, realDest)
  460. err := dirCopy(srcFsh, src, destFsh, realDest, progressUpdate)
  461. if err != nil {
  462. return err
  463. } else {
  464. //Move completed. Remove source file.
  465. srcAbst.RemoveAll(src)
  466. return nil
  467. }
  468. } else {
  469. //Source is file only. Copy file.
  470. realDest := filepath.Join(dest, movedFilename)
  471. /*
  472. Updates 20-10-2020, replaced io.Copy to BufferedLargeFileCopy
  473. Legacy code removed.
  474. */
  475. //Update the progress
  476. if progressUpdate != nil {
  477. progressUpdate(100, filepath.Base(src))
  478. }
  479. f, err := srcAbst.ReadStream(src)
  480. if err != nil {
  481. fmt.Println(err)
  482. return err
  483. }
  484. err = destAbst.WriteStream(realDest, f, 0755)
  485. if err != nil {
  486. fmt.Println(err)
  487. return err
  488. }
  489. //Delete the source file after copy
  490. err = srcAbst.Remove(src)
  491. counter := 0
  492. for err != nil {
  493. //Sometime Windows need this to prevent windows caching bring problems to file remove
  494. time.Sleep(1 * time.Second)
  495. os.Remove(src)
  496. counter++
  497. log.Println("Retrying to remove file: " + src)
  498. if counter > 10 {
  499. return errors.New("Source file remove failed.")
  500. }
  501. }
  502. }
  503. return nil
  504. }
  505. //Copy a given directory, with no progress udpate
  506. func CopyDir(srcFsh *FileSystemHandler, src string, destFsh *FileSystemHandler, dest string) error {
  507. return dirCopy(srcFsh, src, destFsh, dest, func(progress int, name string) {})
  508. }
  509. //Replacment of the legacy dirCopy plugin with filepath.Walk function. Allowing real time progress update to front end
  510. func dirCopy(srcFsh *FileSystemHandler, src string, destFsh *FileSystemHandler, realDest string, progressUpdate func(int, string)) error {
  511. srcFshAbs := srcFsh.FileSystemAbstraction
  512. destFshAbs := destFsh.FileSystemAbstraction
  513. //Get the total file counts
  514. totalFileCounts := int64(0)
  515. srcFshAbs.Walk(src, func(path string, info os.FileInfo, err error) error {
  516. if !info.IsDir() {
  517. //Updates 22 April 2021, chnaged from file count to file size for progress update
  518. //totalFileCounts++
  519. totalFileCounts += info.Size()
  520. }
  521. return nil
  522. })
  523. //Make the destinaton directory
  524. if !destFshAbs.FileExists(realDest) {
  525. destFshAbs.Mkdir(realDest, 0755)
  526. }
  527. //Start moving
  528. fileCounter := int64(0)
  529. src = filepath.ToSlash(src)
  530. err := srcFshAbs.Walk(src, func(path string, info os.FileInfo, err error) error {
  531. path = filepath.ToSlash(path)
  532. var folderRootRelative string = strings.TrimPrefix(path, src)
  533. if folderRootRelative == "" {
  534. return nil
  535. }
  536. if info.IsDir() {
  537. //Mkdir base on relative path
  538. return destFshAbs.MkdirAll(filepath.Join(realDest, folderRootRelative), 0755)
  539. } else {
  540. //fileCounter++
  541. fileCounter += info.Size()
  542. //Move file base on relative path
  543. fileSrc := filepath.ToSlash(filepath.Join(filepath.Clean(src), folderRootRelative))
  544. fileDest := filepath.ToSlash(filepath.Join(filepath.Clean(realDest), folderRootRelative))
  545. //Update move progress
  546. if progressUpdate != nil {
  547. progressUpdate(int(float64(fileCounter)/float64(totalFileCounts)*100), filepath.Base(fileSrc))
  548. }
  549. //Move the file using BLFC
  550. f, err := srcFshAbs.ReadStream(fileSrc)
  551. if err != nil {
  552. fmt.Println(err)
  553. return err
  554. }
  555. err = destFshAbs.WriteStream(fileDest, f, 0755)
  556. if err != nil {
  557. fmt.Println(err)
  558. return err
  559. }
  560. }
  561. return nil
  562. })
  563. return err
  564. }
  565. func BasicFileCopy(src string, dst string) error {
  566. sourceFileStat, err := os.Stat(src)
  567. if err != nil {
  568. return err
  569. }
  570. if !sourceFileStat.Mode().IsRegular() {
  571. return fmt.Errorf("%s is not a regular file", src)
  572. }
  573. source, err := os.Open(src)
  574. if err != nil {
  575. return err
  576. }
  577. defer source.Close()
  578. destination, err := os.Create(dst)
  579. if err != nil {
  580. return err
  581. }
  582. defer destination.Close()
  583. _, err = io.Copy(destination, source)
  584. return err
  585. }
  586. //Use for copying large file using buffering method. Allowing copying large file with little RAM
  587. func BufferedLargeFileCopy(src string, dst string, BUFFERSIZE int64) error {
  588. sourceFileStat, err := os.Stat(src)
  589. if err != nil {
  590. return err
  591. }
  592. if !sourceFileStat.Mode().IsRegular() {
  593. return errors.New("Invalid file source")
  594. }
  595. source, err := os.Open(src)
  596. if err != nil {
  597. return err
  598. }
  599. destination, err := os.Create(dst)
  600. if err != nil {
  601. return err
  602. }
  603. buf := make([]byte, BUFFERSIZE)
  604. for {
  605. n, err := source.Read(buf)
  606. if err != nil && err != io.EOF {
  607. source.Close()
  608. destination.Close()
  609. return err
  610. }
  611. if n == 0 {
  612. source.Close()
  613. destination.Close()
  614. break
  615. }
  616. if _, err := destination.Write(buf[:n]); err != nil {
  617. source.Close()
  618. destination.Close()
  619. return err
  620. }
  621. }
  622. return nil
  623. }
  624. func IsDir(path string) bool {
  625. if fileExists(path) == false {
  626. return false
  627. }
  628. fi, err := os.Stat(path)
  629. if err != nil {
  630. log.Fatal(err)
  631. return false
  632. }
  633. switch mode := fi.Mode(); {
  634. case mode.IsDir():
  635. return true
  636. case mode.IsRegular():
  637. return false
  638. }
  639. return false
  640. }
  641. //Unzip tar.gz file
  642. func ExtractTarGzipFile(filename string, outfile string) error {
  643. f, err := os.Open(filename)
  644. if err != nil {
  645. return err
  646. }
  647. err = ExtractTarGzipByStream(filepath.Clean(outfile), f, true)
  648. if err != nil {
  649. return err
  650. }
  651. return f.Close()
  652. }
  653. func ExtractTarGzipByStream(basedir string, gzipStream io.Reader, onErrorResumeNext bool) error {
  654. uncompressedStream, err := gzip.NewReader(gzipStream)
  655. if err != nil {
  656. return err
  657. }
  658. tarReader := tar.NewReader(uncompressedStream)
  659. for {
  660. header, err := tarReader.Next()
  661. if err == io.EOF {
  662. break
  663. }
  664. if err != nil {
  665. return err
  666. }
  667. switch header.Typeflag {
  668. case tar.TypeDir:
  669. err := os.Mkdir(header.Name, 0755)
  670. if err != nil {
  671. if !onErrorResumeNext {
  672. return err
  673. }
  674. }
  675. case tar.TypeReg:
  676. outFile, err := os.Create(filepath.Join(basedir, header.Name))
  677. if err != nil {
  678. if !onErrorResumeNext {
  679. return err
  680. }
  681. }
  682. _, err = io.Copy(outFile, tarReader)
  683. if err != nil {
  684. if !onErrorResumeNext {
  685. return err
  686. }
  687. }
  688. outFile.Close()
  689. default:
  690. //Unknown filetype, continue
  691. }
  692. }
  693. return nil
  694. }