1. 压缩解压文件

1.1.1. 压缩文件

这次介绍的是压缩与解压文件,它的基本原理是创建初始的zip文件,然后循环遍历每个文件,并使用zip编写器将其添加到存档中,确保指定deflate方法以获得更好的压缩。

  1. package main
  2. import (
  3. "archive/zip"
  4. "fmt"
  5. "io"
  6. "os"
  7. )
  8. func main() {
  9. // List of Files to Zip
  10. files := []string{"1.txt", "2.txt"}
  11. output := "done.zip"
  12. if err := ZipFiles(output, files); err != nil {
  13. panic(err)
  14. }
  15. fmt.Println("Zipped File:", output)
  16. }
  17. // ZipFiles compresses one or many files into a single zip archive file.
  18. // Param 1: filename is the output zip file's name.
  19. // Param 2: files is a list of files to add to the zip.
  20. func ZipFiles(filename string, files []string) error {
  21. newZipFile, err := os.Create(filename)
  22. if err != nil {
  23. return err
  24. }
  25. defer newZipFile.Close()
  26. zipWriter := zip.NewWriter(newZipFile)
  27. defer zipWriter.Close()
  28. // Add files to zip
  29. for _, file := range files {
  30. if err = AddFileToZip(zipWriter, file); err != nil {
  31. return err
  32. }
  33. }
  34. return nil
  35. }
  36. func AddFileToZip(zipWriter *zip.Writer, filename string) error {
  37. fileToZip, err := os.Open(filename)
  38. if err != nil {
  39. return err
  40. }
  41. defer fileToZip.Close()
  42. // Get the file information
  43. info, err := fileToZip.Stat()
  44. if err != nil {
  45. return err
  46. }
  47. header, err := zip.FileInfoHeader(info)
  48. if err != nil {
  49. return err
  50. }
  51. // Using FileInfoHeader() above only uses the basename of the file. If we want
  52. // to preserve the folder structure we can overwrite this with the full path.
  53. header.Name = filename
  54. // Change to deflate to gain better compression
  55. // see http://golang.org/pkg/archive/zip/#pkg-constants
  56. header.Method = zip.Deflate
  57. writer, err := zipWriter.CreateHeader(header)
  58. if err != nil {
  59. return err
  60. }
  61. _, err = io.Copy(writer, fileToZip)
  62. return err
  63. }

1.1.2. 解压文件

下面的代码用于将zip存档文件解压缩到其组成文件中。因为可能有多个文件,所以它将在一个文件夹中创建文件(用第二个参数指定)。

  1. package main
  2. import (
  3. "archive/zip"
  4. "fmt"
  5. "io"
  6. "log"
  7. "os"
  8. "path/filepath"
  9. "strings"
  10. )
  11. func main() {
  12. files, err := Unzip("done.zip", "output-folder")
  13. if err != nil {
  14. log.Fatal(err)
  15. }
  16. fmt.Println("Unzipped:\n" + strings.Join(files, "\n"))
  17. }
  18. // Unzip will decompress a zip archive, moving all files and folders
  19. // within the zip file (parameter 1) to an output directory (parameter 2).
  20. func Unzip(src string, dest string) ([]string, error) {
  21. var filenames []string
  22. r, err := zip.OpenReader(src)
  23. if err != nil {
  24. return filenames, err
  25. }
  26. defer r.Close()
  27. for _, f := range r.File {
  28. // Store filename/path for returning and using later on
  29. fpath := filepath.Join(dest, f.Name)
  30. // Check for ZipSlip. More Info: http://bit.ly/2MsjAWE
  31. if !strings.HasPrefix(fpath, filepath.Clean(dest)+string(os.PathSeparator)) {
  32. return filenames, fmt.Errorf("%s: illegal file path", fpath)
  33. }
  34. filenames = append(filenames, fpath)
  35. if f.FileInfo().IsDir() {
  36. // Make Folder
  37. os.MkdirAll(fpath, os.ModePerm)
  38. continue
  39. }
  40. // Make File
  41. if err = os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err != nil {
  42. return filenames, err
  43. }
  44. outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
  45. if err != nil {
  46. return filenames, err
  47. }
  48. rc, err := f.Open()
  49. if err != nil {
  50. return filenames, err
  51. }
  52. _, err = io.Copy(outFile, rc)
  53. // Close the file without defer to close before next iteration of loop
  54. outFile.Close()
  55. rc.Close()
  56. if err != nil {
  57. return filenames, err
  58. }
  59. }
  60. return filenames, nil
  61. }