grokify/mogo

View on GitHub
archive/tarutil/writer.go

Summary

Maintainability
A
40 mins
Test Coverage
package tarutil

import (
    "archive/tar"
    "compress/gzip"
    "io"
    "os"
    "sort"
)

// CreateArchiveGzipFile creates a gzipped tarball with the name `tgzFilename`. `files`
// is a `map[string]string` where the keys are local file paths and the values are
// archive filepaths. If the archive filepath is empty, the local filepath is used.
func CreateArchiveGzipFile(tgzFilename string, files map[string]string) error {
    // Create output file
    out, err := os.Create(tgzFilename)
    if err != nil {
        return err
    }
    defer out.Close()

    // Create the archive and write the output to the "out" Writer
    return CreateArchiveGzipWriter(out, files)
    // Adapted from: https://www.arthurkoziel.com/writing-tar-gz-files-in-go/ by Arthur Koziel.
    // Converted to reusable functions with archive internal filename support.
}

func CreateArchiveGzipWriter(buf io.Writer, files map[string]string) error {
    // Create new Writers for gzip and tar
    // These writers are chained. Writing to the tar writer will
    // write to the gzip writer which in turn will write to
    // the "buf" writer
    gw := gzip.NewWriter(buf)
    defer gw.Close()
    tw := tar.NewWriter(gw)
    defer tw.Close()

    filenames := []string{}
    for filename := range files {
        filenames = append(filenames, filename)
    }
    sort.Strings(filenames)
    // Iterate over files and add them to the tar archive
    for _, filename := range filenames {
        archivename, ok := files[filename]
        if !ok {
            panic("map key not found")
        }
        err := AddFileToArchive(tw, filename, archivename)
        if err != nil {
            return err
        }
    }

    return nil
    // Adapted from: https://www.arthurkoziel.com/writing-tar-gz-files-in-go/ by Arthur Koziel.
    // Converted to reusable functions with archive internal filename support.
}

func AddFileToArchive(tw *tar.Writer, filename, archivename string) error {
    // Open the file which will be written into the archive
    file, err := os.Open(filename)
    if err != nil {
        return err
    }
    defer file.Close()

    // Get FileInfo about our file providing file size, mode, etc.
    info, err := file.Stat()
    if err != nil {
        return err
    }

    // Create a tar Header from the FileInfo data
    header, err := tar.FileInfoHeader(info, info.Name())
    if err != nil {
        return err
    }

    // Use full path as name (FileInfoHeader only takes the basename)
    // If we don't do this the directory strucuture would not be preserved
    // https://golang.org/src/archive/tar/common.go?#L626
    if len(archivename) > 0 {
        header.Name = archivename
    } else {
        header.Name = filename
    }

    // Write file header to the tar archive
    err = tw.WriteHeader(header)
    if err != nil {
        return err
    }

    // Copy file content to tar archive
    _, err = io.Copy(tw, file)
    if err != nil {
        return err
    }
    return tw.Flush()
    // Adapted from: https://www.arthurkoziel.com/writing-tar-gz-files-in-go/ by Arthur Koziel.
    // Converted to reusable functions with archive internal filename support.
}