Golang Zip File Example

Go (Golang) as many other programming languages, supports zip compression out of the box, right from within the standard library. In this article we are going to look at how we can create a zip archive in Go (Golang), and how we can add one or more files into a zip archive. We will be also able to create folders within the same zip archive, which can be useful in certain situations.

When working with zip archives, there is a package from the Go (Golang) standard library called “archive/zip”. The purpose of this package is to provide support for creating and reading zip file archives.

Create your zip archive underlying file

The first thing you might have to do is create the archive as a simple file, like you would do with any other file in Go (Golang). Using the Go os package and the os.Create function

func Create(name string) (*File, error)

Create creates or truncates the named file. If the file already exists, it is truncated. If the file does not exist, it is created with mode 0666 (before umask). If successful, methods on the returned File can be used for I/O; the associated file descriptor has mode O_RDWR. If there is an error, it will be of type *PathError.

Initialise zip archive zip.Writer

From the archive/zip package you will need to use the zip.NewWriter function which will be used to write data (files and directories) to the final zip archive.

func NewWriter(w io.Writer) *Writer

NewWriter returns a new Writer writing a zip file to w.

Add files to the archive using zip.Writer.Create

Once your zip writer is created, you can add files or directories to the zip archive. By using the zip.Writer.Create function

func (w *Writer) Create(name string) (io.Writer, error)

Create adds a file to the zip file using the provided name. It returns a Writer to which the file contents should be written. The file contents will be compressed using the Deflate method. The name must be a relative path: it must not start with a drive letter (e.g. C:) or leading slash, and only forward slashes are allowed. To create a directory instead of a file, add a trailing slash to the name. The file’s contents must be written to the io.Writer before the next call to Create, CreateHeader, or Close.

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

The zip.Writer.Create returns an io.Writer which can be used to write data to, so any file content can be streamed and written into that writer, either by using io.Copy or by just calling the Write function from the io.Writer

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 archive using zip.Writer.Close

Once all the files and directories have been written to the archive, you can close the zip writer and finalise your zip archive, by using the zip.Writer.Close method on your zip archive which will write all the data into the underlying data stream.

func (w *Writer) Close() error

Close finishes writing the zip file by writing the central directory. It does not close the underlying writer.

Golang Zip Archive Code Example

In this code example we try to archive 2 files, 1 txt file and one csv file, into a single zip archive. The files are being archived inside different sub-directories within the archive itself.

package main

import (
    "archive/zip"
    "fmt"
    "io"
    "os"
)

func main() {
    fmt.Println("creating zip archive...")
    archive, err := os.Create("archive.zip")
    if err != nil {
        panic(err)
    }
    defer archive.Close()
    zipWriter := zip.NewWriter(archive)

    fmt.Println("opening first file...")
    f1, err := os.Open("test.csv")
    if err != nil {
        panic(err)
    }
    defer f1.Close()

    fmt.Println("writing first file to archive...")
    w1, err := zipWriter.Create("csv/test.csv")
    if err != nil {
        panic(err)
    }
    if _, err := io.Copy(w1, f1); err != nil {
        panic(err)
    }

    fmt.Println("opening second file")
    f2, err := os.Open("test.txt")
    if err != nil {
        panic(err)
    }
    defer f2.Close()

    fmt.Println("writing second file to archive...")
    w2, err := zipWriter.Create("txt/test.txt")
    if err != nil {
        panic(err)
    }
    if _, err := io.Copy(w2, f2); err != nil {
        panic(err)
    }
    fmt.Println("closing zip archive...")
    zipWriter.Close()
}

This will output the following

creating zip archive...
opening first file...
writing first file to archive...
opening second file
writing second file to archive...
closing zip archive...

The final zip archive contains the files as expected

$ unzip -l archive.zip
Archive:  archive.zip
Length  Date  Time  Name
---------  ---------- ----- ----
57  00-00-1980 00:00 csv/test.csv
12  00-00-1980 00:00 txt/test.txt
--------- -------
69 2 files
Join the Golang Developers Community on Golang Cafe