Introduction

Go, with its efficient standard library, makes zipping and unzipping files straightforward and efficient. Whether you're working with compressed archives for application packaging, data backup, or simply organizing and transferring files, knowing how to zip and unzip files within Go applications is invaluable.

Zipping Files and Folders

To zip files and folders in Go, we will use the 'archive/zip' package, which provides convenient functions to handle ZIP archives. Let's jump into the implementation.

Zipping a Single File

Below is a step-by-step guide to zipping a single file:


package main

import (
    "archive/zip"
    "fmt"
    "io"
    "log"
    "os"
    "path/filepath"
)

func main() {
    fileToZip := "example.txt"
    zipFileName := "example.zip"
    err := zipFiles(zipFileName, []string{fileToZip})
    if err != nil {
        log.Fatalf("Failed to zip file: %v", err)
    }
    fmt.Printf("Successfully zipped %s to %s\n", fileToZip, zipFileName)
}

func zipFiles(zipFileName string, files []string) error {
    newZipFile, err := os.Create(zipFileName)
    if err != nil {
        return err
    }
    defer newZipFile.Close()

    zipWriter := zip.NewWriter(newZipFile)
    defer zipWriter.Close()

    for _, file := range files {
        if err := addFileToZip(zipWriter, file); err != nil {
            return err
        }
    }
    return nil
}

func addFileToZip(zipWriter *zip.Writer, fileName string) error {
    fileToZip, err := os.Open(fileName)
    if err != nil {
        return err
    }
    defer fileToZip.Close()

    info, err := fileToZip.Stat()
    if err != nil {
        return err
    }

    header, err := zip.FileInfoHeader(info)
    if err != nil {
        return err
    }
    header.Name = filepath.Base(fileName)
    header.Method = zip.Deflate

    writer, err := zipWriter.CreateHeader(header)
    if err != nil {
        return err
    }
    _, err = io.Copy(writer, fileToZip)
    return err
}

Zipping a Folder

To zip an entire folder, we'll recursively discover files, adding them to the zip. Here's how you can achieve that:


package main

import (
    "archive/zip"
    "fmt"
    "io"
    "log"
    "os"
    "path/filepath"
    "strings"
)

func main() {
    folderToZip := "folderName"
    zipFileName := "folder.zip"
    err := zipFolder(folderToZip, zipFileName)
    if err != nil {
        log.Fatalf("Failed to zip folder: %v", err)
    }
    fmt.Printf("Successfully zipped folder %s to %s\n", folderToZip, zipFileName)
}

func zipFolder(source, target string) error {
    file, err := os.Create(target)
    if err != nil {
        return err
    }
    defer file.Close()

    zipWriter := zip.NewWriter(file)
    defer zipWriter.Close()

    baseFolder := filepath.Base(source)

    return filepath.Walk(source, func(path string, info os.FileInfo, err error) error {
        if err != nil {
            return err
        }
        if path == source {
            return nil
        }

        header, err := zip.FileInfoHeader(info)
        if err != nil {
            return err
        }
        header.Name = filepath.Join(baseFolder, strings.TrimPrefix(path, source))

        if info.IsDir() {
            header.Name += "/"
            _, err = zipWriter.CreateHeader(header)
            return err
        }

        file, err := os.Open(path)
        if err != nil {
            return err
        }
        defer file.Close()

        writer, err := zipWriter.CreateHeader(header)
        if err != nil {
            return err
        }

        _, err = io.Copy(writer, file)
        return err
    })
}

Unzipping Files

To unzip files using Go, again, the 'archive/zip' package comes handy. Here's how you can extract files from a ZIP archive.


package main

import (
    "archive/zip"
    "fmt"
    "io"
    "log"
    "os"
    "path/filepath"
    "strings"
)

func main() {
    zipFileName := "example.zip"
    outputDir := "extracted"
    err := unzip(zipFileName, outputDir)
    if err != nil {
        log.Fatalf("Failed to unzip file: %v", err)
    }
    fmt.Printf("Successfully extracted %s to directory %s\n", zipFileName, outputDir)
}

func unzip(src string, dest string) error {
    r, err := zip.OpenReader(src)
    if err != nil {
        return err
    }
    defer r.Close()

    for _, f := range r.File {
        fpath := filepath.Join(dest, f.Name)
        if !strings.HasPrefix(fpath, filepath.Clean(dest)+string(os.PathSeparator)) {
            return fmt.Errorf("illegal file path: %s", fpath)
        }
        if f.FileInfo().IsDir() {
            os.MkdirAll(fpath, os.ModePerm)
            continue
        }

        os.MkdirAll(filepath.Dir(fpath), os.ModePerm)

        outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
        if err != nil {
            return err
        }
        
        rc, err := f.Open()
        if err != nil {
            return err
        }

        _, err = io.Copy(outFile, rc)
        outFile.Close()
        rc.Close()

        if err != nil {
            return err
        }
    }
    return nil
}

Conclusion

In this article, we've covered how to create ZIP archives and extract their contents using Go. Mastering these techniques allows for better file handling in your applications, promoting efficient storage and transfer solutions.