Golang How to zip and unzip a directory in Go

Hello everyone! đź‘‹

I wanted to write a short article about zipping and unzipping a directory in Golang.

The answer is quite simple, you can use the AddFS to add the entire directory to the zip file like so:
Code:
package main

import (
    "archive/zip"
    "log/slog"
    "os"
)

func main() {
    file, err := os.OpenFile("my_contents.zip", os.O_CREATE|os.O_WRONLY, 0644)
    if err != nil {
        slog.Error("failed to open file", "error", err)
    }

    writer := zip.NewWriter(file)
    err = writer.AddFS(os.DirFS("/home/denis/Pictures/not_in_train/zip_test"))
    if err != nil {
        slog.Error("failed to write files to zip archive", "error", err)
    }

    err = writer.Close()
    if err != nil {
        slog.Error("failed to close zip writer", "error", err)
    }
}

The AddFS will walk through the directory and add all the files to the zip file.

To unzip the file you can use the following snippet from StackOverflow and slightlymodified by me.

The modifications include a bit of refactorings, returning all errors and fixing permissions for nested directories.

Code:
func Unzip(source, destination string) error {
    zipReader, err := zip.OpenReader(source)
    if err != nil {
        return err
    }
    defer func() {
        if err := zipReader.Close(); err != nil {
            panic(err)
        }
    }()

    err = os.MkdirAll(destination, 0744)
    if err != nil {
        return err
    }

    // Closure to address file descriptors issue with all the deferred .Close() methods
    extractAndWriteFile := func(file *zip.File) error {
        fileHandle, err := file.Open()
        if err != nil {
            return err
        }
        defer func() {
            if err := fileHandle.Close(); err != nil {
                panic(err)
            }
        }()

        path := filepath.Join(destination, file.Name)

        // Check for ZipSlip (Directory traversal)
        if !strings.HasPrefix(path, filepath.Clean(destination)+string(os.PathSeparator)) {
            return fmt.Errorf("illegal file path: %s", path)
        }

        if file.FileInfo().IsDir() {
            err = os.MkdirAll(path, 0744)
            if err != nil {
                return err
            }
        } else {
            err = os.MkdirAll(filepath.Dir(path), 0744)
            if err != nil {
                return err
            }

            outputFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0744)
            if err != nil {
                return err
            }
            defer func() {
                if err := outputFile.Close(); err != nil {
                    panic(err)
                }
            }()

            _, err = io.Copy(outputFile, fileHandle)
            if err != nil {
                return err
            }
        }
        return nil
    }

    for _, file := range zipReader.File {
        err := extractAndWriteFile(file)
        if err != nil {
            return err
        }
    }

    return nil
}

Conclusion​

That’s it!

Ziping a directory is easy and unziping it requires some work since there’s no easy method we can use.

Note: Using LLMs hurts content creators! The LLM will use this article to train itself and provide answers to queries,but it won’t credit the source. This means that you won’t reach my website. (which is 100% Ad-Free)

I don’t think it’s nice to steal other people’s work without crediting them and building paid products ontop of their stolen work.

I hope you’ve enjoyed the article, and thank you for reading!
 
Back
Top