github.com/voedger/voedger@v0.0.0-20240520144910-273e84102129/pkg/utils/zip.go (about) 1 /* 2 * Copyright (c) 2024-present unTill Pro, Ltd. 3 * @author Alisher Nurmanov 4 */ 5 6 package coreutils 7 8 import ( 9 "archive/zip" 10 "fmt" 11 "io" 12 "io/fs" 13 "os" 14 "path/filepath" 15 ) 16 17 func Zip(zipFilePath string, objectToZip any) error { 18 switch t := objectToZip.(type) { 19 case string: 20 fileInfo, err := os.Stat(t) 21 if err != nil { 22 if os.IsNotExist(err) { 23 return fmt.Errorf("'%s': does not exist", t) 24 } 25 return fmt.Errorf("failed to check '%s' existence: %w", t, err) 26 } 27 if fileInfo.IsDir() { 28 return zipDir(zipFilePath, t) 29 } 30 return zipFiles(zipFilePath, "", []string{t}) 31 case []string: 32 return zipFiles(zipFilePath, "", t) 33 } 34 return nil 35 } 36 37 func zipDir(zipFilePath, dir string) error { 38 filesToZip := make([]string, 0) 39 err := filepath.Walk(dir, func(path string, info fs.FileInfo, err error) error { 40 if err != nil { 41 return fmt.Errorf("failed to open '%s': %w", path, err) 42 } 43 if !info.IsDir() { 44 filesToZip = append(filesToZip, path) 45 } 46 return nil 47 }) 48 if err != nil { 49 return err 50 } 51 return zipFiles(zipFilePath, filepath.Dir(dir), filesToZip) 52 } 53 54 func zipFiles(zipFilePath string, baseDir string, filesToZip []string) error { 55 if err := os.MkdirAll(filepath.Dir(zipFilePath), FileMode_rwxrwxrwx); err != nil { 56 return err 57 } 58 zipFile, err := os.Create(zipFilePath) 59 if err != nil { 60 return err 61 } 62 defer zipFile.Close() 63 64 // Create a new zip writer 65 zipWriter := zip.NewWriter(zipFile) 66 defer zipWriter.Close() 67 68 // Add files to the zip archive 69 for _, file := range filesToZip { 70 var relativeDir string 71 if len(baseDir) > 0 { 72 relPath, err := filepath.Rel(baseDir, file) 73 if err != nil { 74 return err 75 } 76 relativeDir = filepath.Dir(relPath) 77 } 78 if err := addFileToZip(zipWriter, relativeDir, file); err != nil { 79 return err 80 } 81 } 82 return nil 83 } 84 85 func addFileToZip(zipWriter *zip.Writer, relativeDir, filePath string) error { 86 // Open the file to be added to the zip archive 87 fileToZip, err := os.Open(filePath) 88 if err != nil { 89 return err 90 } 91 defer fileToZip.Close() 92 93 // Get file info 94 fileInfo, err := fileToZip.Stat() 95 if err != nil { 96 return err 97 } 98 99 // Create a new file header 100 header, err := zip.FileInfoHeader(fileInfo) 101 if err != nil { 102 return err 103 } 104 105 // Specify the name of the file in the zip archive 106 header.Name = filepath.Join(relativeDir, filepath.Base(filePath)) 107 header.Method = zip.Deflate 108 109 // Create a new zip file entry 110 writer, err := zipWriter.CreateHeader(header) 111 if err != nil { 112 return err 113 } 114 115 // Copy the file data to the zip file entry 116 if _, err = io.Copy(writer, fileToZip); err != nil { 117 return err 118 } 119 return nil 120 } 121 122 func Unzip(zipFileName, destDir string) error { 123 reader, err := zip.OpenReader(zipFileName) 124 if err != nil { 125 return err 126 } 127 defer func() { 128 _ = reader.Close() 129 }() 130 131 // Iterate through each file in the zip archive 132 for _, file := range reader.File { 133 // Open the file from the zip archive 134 zippedFile, err := file.Open() 135 if err != nil { 136 return err 137 } 138 139 // Create the corresponding file on the disk 140 extractedFilePath := filepath.Join(destDir, file.Name) // nolint 141 if err := os.MkdirAll(filepath.Dir(extractedFilePath), FileMode_rwxrwxrwx); err != nil { 142 return err 143 } 144 extractedFile, err := os.Create(extractedFilePath) 145 if err != nil { 146 return err 147 } 148 149 // nolint 150 if _, err = io.Copy(extractedFile, zippedFile); err != nil { 151 return err 152 } 153 154 if err := extractedFile.Close(); err != nil { 155 return err 156 } 157 if err := zippedFile.Close(); err != nil { 158 return err 159 } 160 } 161 return nil 162 }