fileOpr.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810
  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. defer f.Close()
  383. err = destFshAbs.WriteStream(realDest, f, 0775)
  384. if err != nil {
  385. return err
  386. }
  387. if progressUpdate != nil {
  388. //Set progress to 100, leave it to upper level abstraction to handle
  389. progressUpdate(100, filepath.Base(realDest))
  390. }
  391. }
  392. return nil
  393. }
  394. func FileMove(srcFsh *FileSystemHandler, src string, destFsh *FileSystemHandler, dest string, mode string, fastMove bool, progressUpdate func(int, string)) error {
  395. srcAbst := srcFsh.FileSystemAbstraction
  396. destAbst := destFsh.FileSystemAbstraction
  397. src = filepath.ToSlash(src)
  398. dest = filepath.ToSlash(dest)
  399. if srcAbst.IsDir(src) && strings.HasPrefix(dest, src) {
  400. //Recursive operation. Reject
  401. return errors.New("Recursive move operation.")
  402. }
  403. if !destAbst.FileExists(dest) {
  404. if destAbst.FileExists(filepath.Dir(dest)) {
  405. //User pass in the whole path for the folder. Report error usecase.
  406. return errors.New("Dest location should be an existing folder instead of the full path of the moved file.")
  407. }
  408. os.MkdirAll(dest, 0775)
  409. }
  410. //Check if the target file already exists.
  411. movedFilename := filepath.Base(src)
  412. if destAbst.FileExists(filepath.Join(dest, filepath.Base(src))) {
  413. //Handle cases where file already exists
  414. if mode == "" {
  415. //Do not specific file exists principle
  416. return errors.New("Destination file already exists.")
  417. } else if mode == "skip" {
  418. //Skip this file
  419. return nil
  420. } else if mode == "overwrite" {
  421. //Continue with the following code
  422. //Check if the copy and paste dest are identical
  423. if filepath.ToSlash(filepath.Clean(src)) == filepath.ToSlash(filepath.Clean(filepath.Join(dest, filepath.Base(src)))) {
  424. //Source and target identical. Cannot overwrite.
  425. return errors.New("Source and destination paths are identical.")
  426. }
  427. } else if mode == "keep" {
  428. //Keep the file but saved with 'Copy' suffix
  429. newFilename := strings.TrimSuffix(filepath.Base(src), filepath.Ext(src)) + " - Copy" + filepath.Ext(src)
  430. //Check if the newFilename already exists. If yes, continue adding suffix
  431. duplicateCounter := 0
  432. for destAbst.FileExists(filepath.Join(dest, newFilename)) {
  433. duplicateCounter++
  434. newFilename = strings.TrimSuffix(filepath.Base(src), filepath.Ext(src)) + " - Copy(" + strconv.Itoa(duplicateCounter) + ")" + filepath.Ext(src)
  435. if duplicateCounter > 1024 {
  436. //Maxmium loop encountered. For thread safty, terminate here
  437. return errors.New("Too many copies of identical files.")
  438. }
  439. }
  440. movedFilename = newFilename
  441. } else {
  442. //This exists opr not supported.
  443. return errors.New("Unknown file exists rules given.")
  444. }
  445. }
  446. if fastMove {
  447. //Ready to move with the quick rename method
  448. realDest := filepath.Join(dest, movedFilename)
  449. err := os.Rename(src, realDest)
  450. if err == nil {
  451. //Fast move success
  452. return nil
  453. }
  454. //Fast move failed. Back to the original copy and move method
  455. }
  456. //Ready to move. Check if both folder are located in the same root devices. If not, use copy and delete method.
  457. if srcAbst.IsDir(src) {
  458. //Source file is directory. CopyFolder
  459. realDest := filepath.Join(dest, movedFilename)
  460. //err := dircpy.Copy(src, realDest)
  461. err := dirCopy(srcFsh, src, destFsh, realDest, progressUpdate)
  462. if err != nil {
  463. return err
  464. } else {
  465. //Move completed. Remove source file.
  466. srcAbst.RemoveAll(src)
  467. return nil
  468. }
  469. } else {
  470. //Source is file only. Copy file.
  471. realDest := filepath.Join(dest, movedFilename)
  472. /*
  473. Updates 20-10-2020, replaced io.Copy to BufferedLargeFileCopy
  474. Legacy code removed.
  475. */
  476. //Update the progress
  477. if progressUpdate != nil {
  478. progressUpdate(100, filepath.Base(src))
  479. }
  480. f, err := srcAbst.ReadStream(src)
  481. if err != nil {
  482. fmt.Println(err)
  483. return err
  484. }
  485. defer f.Close()
  486. err = destAbst.WriteStream(realDest, f, 0755)
  487. if err != nil {
  488. fmt.Println(err)
  489. return err
  490. }
  491. //Delete the source file after copy
  492. err = srcAbst.Remove(src)
  493. counter := 0
  494. for err != nil {
  495. //Sometime Windows need this to prevent windows caching bring problems to file remove
  496. time.Sleep(1 * time.Second)
  497. os.Remove(src)
  498. counter++
  499. log.Println("Retrying to remove file: " + src)
  500. if counter > 10 {
  501. return errors.New("Source file remove failed.")
  502. }
  503. }
  504. }
  505. return nil
  506. }
  507. //Copy a given directory, with no progress udpate
  508. func CopyDir(srcFsh *FileSystemHandler, src string, destFsh *FileSystemHandler, dest string) error {
  509. return dirCopy(srcFsh, src, destFsh, dest, func(progress int, name string) {})
  510. }
  511. //Replacment of the legacy dirCopy plugin with filepath.Walk function. Allowing real time progress update to front end
  512. func dirCopy(srcFsh *FileSystemHandler, src string, destFsh *FileSystemHandler, realDest string, progressUpdate func(int, string)) error {
  513. srcFshAbs := srcFsh.FileSystemAbstraction
  514. destFshAbs := destFsh.FileSystemAbstraction
  515. //Get the total file counts
  516. totalFileCounts := int64(0)
  517. srcFshAbs.Walk(src, func(path string, info os.FileInfo, err error) error {
  518. if !info.IsDir() {
  519. //Updates 22 April 2021, chnaged from file count to file size for progress update
  520. //totalFileCounts++
  521. totalFileCounts += info.Size()
  522. }
  523. return nil
  524. })
  525. //Make the destinaton directory
  526. if !destFshAbs.FileExists(realDest) {
  527. destFshAbs.Mkdir(realDest, 0755)
  528. }
  529. //Start moving
  530. fileCounter := int64(0)
  531. src = filepath.ToSlash(src)
  532. err := srcFshAbs.Walk(src, func(path string, info os.FileInfo, err error) error {
  533. path = filepath.ToSlash(path)
  534. var folderRootRelative string = strings.TrimPrefix(path, src)
  535. if folderRootRelative == "" {
  536. return nil
  537. }
  538. if info.IsDir() {
  539. //Mkdir base on relative path
  540. return destFshAbs.MkdirAll(filepath.Join(realDest, folderRootRelative), 0755)
  541. } else {
  542. //fileCounter++
  543. fileCounter += info.Size()
  544. //Move file base on relative path
  545. fileSrc := filepath.ToSlash(filepath.Join(filepath.Clean(src), folderRootRelative))
  546. fileDest := filepath.ToSlash(filepath.Join(filepath.Clean(realDest), folderRootRelative))
  547. //Update move progress
  548. if progressUpdate != nil {
  549. progressUpdate(int(float64(fileCounter)/float64(totalFileCounts)*100), filepath.Base(fileSrc))
  550. }
  551. //Move the file using BLFC
  552. f, err := srcFshAbs.ReadStream(fileSrc)
  553. if err != nil {
  554. fmt.Println(err)
  555. return err
  556. }
  557. defer f.Close()
  558. err = destFshAbs.WriteStream(fileDest, f, 0755)
  559. if err != nil {
  560. fmt.Println(err)
  561. return err
  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. }