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  }