Golang Unzip File Example

In another article, we have covered how you can zip files with Go (Golang). In this article, we are going to look instead, at how you can decompress a zip archive by using Go (Golang) and just tools from the Go standard library.

When working with zip archives, there is a package from the Go (Golang) standard library called “archive/zip”. The same package can be used both for archiving and un-archiving (decompressing) zip archives. It’s very minimal and easy to use. Let’s have a look at how we can unzip files in Go (Golang)

Open the zip archive using zip.OpenReader

The first thing we might need to do is to open the archive we want to decompress. In order to do that, you can use the method provided in the archive/zip standard library package, which allows you to open up an archive and returns a io.ReadCloser which is an instance of zip.Reader. The method we are going to use is called zip.OpenReader

func OpenReader(name string) (*ReadCloser, error)

OpenReader will open the Zip file specified by name and return a ReadCloser.

Loop through zip.Reader’s files

Once zip.OpenReader is called, a io.ReadCloser is returned. This is an instance of zip.Reader which as you can see below, it has a slice of zip.File

type Reader struct {
    File    []*File
    Comment string
    // contains filtered or unexported fields
}

The slice of zip.File can be used to navigate through the archive contents.

Read zip archive file contents by using zip.File.Open

Once your zip archive is opened, and you have a list of files to go through, you can use zip.File.Open to read file’s content and write it somewhere else (your decompressed output files)

func (f *File) Open() (io.ReadCloser, error)

Open returns a ReadCloser that provides access to the File’s contents. Multiple files may be read concurrently.

Save decompressed files’ contents using io.Copy or io.Writer.Write

The zip.File.Open returns an io.ReadCloser which can be used to read data from into a new output file, either by using io.Copy or by just calling the Read function from the io.ReadCloser.

func Copy(dst Writer, src Reader) (written int64, err error)

Copy copies from src to dst until either EOF is reached on src or an error occurs. It returns the number of bytes copied and the first error encountered while copying, if any.

Close zip files using zip.Reader.Close

Every time you read a file’s content from the zip archive and copy it into your output, decompressed direcotry, you should close the file handler, by using the Close function from the io.ReadCloser.

type ReadCloser interface {
    Reader
    Closer
}

// Closer is the interface that wraps the basic Close method.
// The behavior of Close after the first call is undefined. 
// Specific implementations may document their own behavior.

type Closer interface {
    Close() error
}

Golang Unzip File Code Example

In this code example we try to decompress a zip archive that contains 2 files. There is 1 txt file and one csv file, which are going to be decompressed into a chosen outputdirectory

package main

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

func main() {
    dst := "output"
    archive, err := zip.OpenReader("archive.zip")
    if err != nil {
        panic(err)
    }
    defer archive.Close()

    for _, f := range archive.File {
        filePath := filepath.Join(dst, f.Name)
        fmt.Println("unzipping file ", filePath)

        if !strings.HasPrefix(filePath, filepath.Clean(dst)+string(os.PathSeparator)) {
            fmt.Println("invalid file path")
            return
        }
        if f.FileInfo().IsDir() {
            fmt.Println("creating directory...")
            os.MkdirAll(filePath, os.ModePerm)
            continue
        }

        if err := os.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil {
            panic(err)
        }

        dstFile, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
        if err != nil {
            panic(err)
        }

        fileInArchive, err := f.Open()
        if err != nil {
            panic(err)
        }

        if _, err := io.Copy(dstFile, fileInArchive); err != nil {
            panic(err)
        }

        dstFile.Close()
        fileInArchive.Close()
    }
}

This will output the following

unzipping file  output/csv/test.csv
unzipping file  output/txt/test.txt
Join the Golang Developers Community on Golang Cafe