is there a easy way to unzip file with golang ?
right now my code is:
func Unzip(src, dest string) error { r, err := zip.OpenReader(src) if err != nil { return err } defer r.Close() for _, f := range r.File { rc, err := f.Open() if err != nil { return err } defer rc.Close() path := filepath.Join(dest, f.Name) if f.FileInfo().IsDir() { os.MkdirAll(path, f.Mode()) } else { f, err := os.OpenFile( path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode()) if err != nil { return err } defer f.Close() _, err = io.Copy(f, rc) if err != nil { return err } } } return nil }
To extract the files from a ZIP file, use the unzip command, and provide the name of the ZIP file. Note that you do need to provide the “. zip” extension. As the files are extracted they are listed to the terminal window.
Slight rework of the OP's solution to create the containing directory dest
if it doesn't exist, and to wrap the file extraction/writing in a closure to eliminate stacking of defer .Close()
calls per @Nick Craig-Wood's comment:
func Unzip(src, dest string) error { r, err := zip.OpenReader(src) if err != nil { return err } defer func() { if err := r.Close(); err != nil { panic(err) } }() os.MkdirAll(dest, 0755) // Closure to address file descriptors issue with all the deferred .Close() methods extractAndWriteFile := func(f *zip.File) error { rc, err := f.Open() if err != nil { return err } defer func() { if err := rc.Close(); err != nil { panic(err) } }() path := filepath.Join(dest, f.Name) // Check for ZipSlip (Directory traversal) if !strings.HasPrefix(path, filepath.Clean(dest) + string(os.PathSeparator)) { return fmt.Errorf("illegal file path: %s", path) } if f.FileInfo().IsDir() { os.MkdirAll(path, f.Mode()) } else { os.MkdirAll(filepath.Dir(path), f.Mode()) f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode()) if err != nil { return err } defer func() { if err := f.Close(); err != nil { panic(err) } }() _, err = io.Copy(f, rc) if err != nil { return err } } return nil } for _, f := range r.File { err := extractAndWriteFile(f) if err != nil { return err } } return nil }
Note: Updated to include Close() error handling as well (if we're looking for best practices, may as well follow ALL of them).
I'm using archive/zip
package to read .zip files and copy to the local disk. Below is the source code to unzip .zip files for my own needs.
import ( "archive/zip" "io" "log" "os" "path/filepath" "strings" ) func unzip(src, dest string) error { r, err := zip.OpenReader(src) if err != nil { return err } defer r.Close() for _, f := range r.File { rc, err := f.Open() if err != nil { return err } defer rc.Close() fpath := filepath.Join(dest, f.Name) if f.FileInfo().IsDir() { os.MkdirAll(fpath, f.Mode()) } else { var fdir string if lastIndex := strings.LastIndex(fpath,string(os.PathSeparator)); lastIndex > -1 { fdir = fpath[:lastIndex] } err = os.MkdirAll(fdir, f.Mode()) if err != nil { log.Fatal(err) return err } f, err := os.OpenFile( fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode()) if err != nil { return err } defer f.Close() _, err = io.Copy(f, rc) if err != nil { return err } } } return nil }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With