Skip to content

Instantly share code, notes, and snippets.

@svett

svett/compress.go

Last active May 12, 2021
Embed
What would you like to do?
ZIP archives in Golang
import (
"archive/zip"
"io"
"os"
"path/filepath"
"strings"
)
func zipit(source, target string) error {
zipfile, err := os.Create(target)
if err != nil {
return err
}
defer zipfile.Close()
archive := zip.NewWriter(zipfile)
defer archive.Close()
info, err := os.Stat(source)
if err != nil {
return nil
}
var baseDir string
if info.IsDir() {
baseDir = filepath.Base(source)
}
filepath.Walk(source, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
header, err := zip.FileInfoHeader(info)
if err != nil {
return err
}
if baseDir != "" {
header.Name = filepath.Join(baseDir, strings.TrimPrefix(path, source))
}
if info.IsDir() {
header.Name += "/"
} else {
header.Method = zip.Deflate
}
writer, err := archive.CreateHeader(header)
if err != nil {
return err
}
if info.IsDir() {
return nil
}
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()
_, err = io.Copy(writer, file)
return err
})
return err
}
import (
"archive/zip"
"io"
"os"
"path/filepath"
"strings"
)
func unzip(archive, target string) error {
reader, err := zip.OpenReader(archive)
if err != nil {
return err
}
if err := os.MkdirAll(target, 0755); err != nil {
return err
}
for _, file := range reader.File {
path := filepath.Join(target, file.Name)
if file.FileInfo().IsDir() {
os.MkdirAll(path, file.Mode())
continue
}
fileReader, err := file.Open()
if err != nil {
return err
}
defer fileReader.Close()
targetFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode())
if err != nil {
return err
}
defer targetFile.Close()
if _, err := io.Copy(targetFile, fileReader); err != nil {
return err
}
}
return nil
}
@yhirose

This comment has been minimized.

Copy link

@yhirose yhirose commented Jan 23, 2016

@svett, thanks for the very useful code. I have a question on line 21 in compress.go. Is there any reason to return nil instead of err in this case?

@rande

This comment has been minimized.

Copy link

@rande rande commented Mar 3, 2016

The extract function will generate too many open file issue as the defer is only called once the function return, here an altered version:

func Unzip(archive, target string) error {
    reader, err := zip.OpenReader(archive)
    if err != nil {
        return err
    }

    if err := os.MkdirAll(target, 0755); err != nil {
        return err
    }

    for _, file := range reader.File {
        path := filepath.Join(target, file.Name)
        if file.FileInfo().IsDir() {
            os.MkdirAll(path, file.Mode())
            continue
        }

        fileReader, err := file.Open()
        if err != nil {

            if fileReader != nil {
                fileReader.Close()
            }

            return err
        }

        targetFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode())
        if err != nil {
            fileReader.Close()

            if targetFile != nil {
                targetFile.Close()
            }

            return err
        }

        if _, err := io.Copy(targetFile, fileReader); err != nil {
            fileReader.Close()
            targetFile.Close()

            return err
        }

        fileReader.Close()
        targetFile.Close()
    }

    return nil
}
@dopatraman

This comment has been minimized.

Copy link

@dopatraman dopatraman commented May 25, 2016

The zipit function throws an error if the .zip file does not already exist. How do i remedy this?

@anismiles

This comment has been minimized.

Copy link

@anismiles anismiles commented Jun 5, 2017

	// Check file
	if _, err := os.Stat(archive); os.IsNotExist(err) {
		return err
	}
@hrieke

This comment has been minimized.

Copy link

@hrieke hrieke commented Oct 19, 2017

License?

@blacklat

This comment has been minimized.

Copy link

@blacklat blacklat commented Apr 2, 2020

package main

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

func main() {
	if err := foo("test.zip"); err != nil {
		fmt.Println(err)
	}
}

func foo(yourZipFile string) error {
	tmpDir, err := ioutil.TempDir("/tmp", "yourPrefix-")
	if err != nil {
        return err
    }
    defer os.RemoveAll(tmpDir)
	if filenames, err := Unzip(yourZipFile, tmpDir); err != nil {
		return err
	} else {
		for f := range filenames {
			fmt.Println(filenames[f])
		}
	}
	return nil
}


func Unzip(src string, dst string) ([]string, error) {
    var filenames []string
    r, err := zip.OpenReader(src)
    if err != nil {
        return nil, err
    }
    defer r.Close()
    for f := range r.File {
        dstpath := filepath.Join(dst, r.File[f].Name)
        if !strings.HasPrefix(dstpath, filepath.Clean(dst) + string(os.PathSeparator)) {
            return nil, fmt.Errorf("%s: illegal file path", src)
        }
        if r.File[f].FileInfo().IsDir() {
            if err := os.MkdirAll(dstpath, os.ModePerm); err != nil {
                return nil, err
            }
        } else {
            if rc, err := r.File[f].Open(); err != nil {
                return nil, err
            } else {
                defer rc.Close()
                if of, err := os.OpenFile(dstpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, r.File[f].Mode()); err != nil {
                    return nil, err
                } else {
                    defer of.Close()
                    if _, err = io.Copy(of, rc); err != nil {
                        return nil, err
                    } else {
                        of.Close()
                        rc.Close()
                        filenames = append(filenames, dstpath)
                    }
				}
            }
        }
    }
    if len(filenames) == 0 {
        return nil, fmt.Errorf("zip file is empty")
    }
    return filenames, nil
}
@navono

This comment has been minimized.

Copy link

@navono navono commented Aug 28, 2020

If have a large files in multiple directory, the filepath.Walk would take a long time to process. Any possible to use goroutine to improve performance?

@royalorc

This comment has been minimized.

Copy link

@royalorc royalorc commented Apr 12, 2021

hi all, if I have some sub directories, in the code previous, it just make directory of sub directories, how can i restore those files in sub directories?

@royalorc

This comment has been minimized.

Copy link

@royalorc royalorc commented Apr 12, 2021

if r.File[f].FileInfo().IsDir() {
if err := os.MkdirAll(dstpath, os.ModePerm); err != nil {
return nil, err
}
}
in this piece of code, how could i copy those files which are in this directory?

@svett

This comment has been minimized.

Copy link
Owner Author

@svett svett commented Apr 13, 2021

You have to walk through the directories until you reach a regular file.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment