Sling Academy
Home/Golang/How to zip/ unzip a file or folder in Go

How to zip/ unzip a file or folder in Go

Last updated: November 26, 2024

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.

Next Article: Go: How to get filename & extension from a path

Previous Article: How to list all files and subfolders in a folder in Go

Series: File I/O and Operating System Interaction

Golang

Related Articles

You May Also Like

  • How to remove HTML tags in a string in Go
  • How to remove special characters in a string in Go
  • How to remove consecutive whitespace in a string in Go
  • How to count words and characters in a string in Go
  • Relative imports in Go: Tutorial & Examples
  • How to run Python code with Go
  • How to generate slug from title in Go
  • How to create an XML sitemap in Go
  • How to redirect in Go (301, 302, etc)
  • Using Go with MongoDB: CRUD example
  • Auto deploy Go apps with CI/ CD and GitHub Actions
  • Fixing Go error: method redeclared with different receiver type
  • Fixing Go error: copy argument must have slice type
  • Fixing Go error: attempted to use nil slice
  • Fixing Go error: assignment to constant variable
  • Fixing Go error: cannot compare X (type Y) with Z (type W)
  • Fixing Go error: method has pointer receiver, not called with pointer
  • Fixing Go error: assignment mismatch: X variables but Y values
  • Fixing Go error: array index must be non-negative integer constant