Last active
April 11, 2025 19:11
-
-
Save paulerickson/6d8650947ee4e3f3dbcc28fde10eaae7 to your computer and use it in GitHub Desktop.
Golang unzip implementation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package unzip | |
import "archive/zip" | |
/** | |
* Extract a zip file named source to directory destination. Handles cases where destination dir… | |
* - does not exist (creates it) | |
* - is empty | |
* - already has source archive extracted into it (files are overwritten) | |
* - has other files in it, not in source archive (not overwritten) | |
* But is expected to fail if it… | |
* - is not writable | |
* - contains a non-empty directory with the same path as a file in source archive (that's not a simple overwrite) | |
*/ | |
func Unzip(source, destination string) error { | |
archive, err := zip.OpenReader(source) | |
if err != nil { | |
return err | |
} | |
defer archive.Close() | |
for _, file := range archive.Reader.File { | |
reader, err := file.Open() | |
if err != nil { | |
return err | |
} | |
defer reader.Close() | |
path := filepath.Join(destination, file.Name) | |
// Remove file if it already exists; no problem if it doesn't; other cases can error out below | |
_ = os.Remove(path) | |
// Create a directory at path, including parents | |
err = os.MkdirAll(path, os.ModePerm) | |
if err != nil { | |
return err | |
} | |
// If file is _supposed_ to be a directory, we're done | |
if file.FileInfo().IsDir() { | |
continue | |
} | |
// otherwise, remove that directory (_not_ including parents) | |
err = os.Remove(path) | |
if err != nil { | |
return err | |
} | |
// and create the actual file. This ensures that the parent directories exist! | |
// An archive may have a single file with a nested path, rather than a file for each parent dir | |
writer, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode()) | |
if err != nil { | |
return err | |
} | |
defer writer.Close() | |
_, err = io.Copy(writer, reader) | |
if err != nil { | |
return err | |
} | |
} | |
return nil | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment