Zipping a directory is unclear and slightly confusing via the archive/zip package, mostly due to the way paths need to be handled and how directories are created. I suggest adding a func to handle zipping a directory simply to aid in implementation. I have had to implement, or educate others on implementing, similar code for various projects over the years. Furthermore, help via StackOverflow, golang-nuts, etc., isn't very clear either. There is clearly somewhat wide-spread confusion regarding zipping a directory.
Pseudo-code is below.
// Dir creates a zip from sourceDirPath and saves it to zipPath. If a file at zipPath
// already exists, an error is returned.
func Dir(sourceDirPath, zipPath string) (err error) {
// Make sure zip file doesn't already exist at zipPath.
_, err = os.Stat(zipFileAbs)
if err == nil {
return os.ErrExist
} else if !os.IsNotExist(err) {
return
}
// Create the zip file.
zipFile, err := os.Create(zipFileAbs)
if err != nil {
return
}
defer zipFile.Close()
// Initialize the zip writer.
z := zip.NewWriter(zipFile)
defer z.Close()
// Zip up the sourceDirPath, files and directories, recursively.
err = filepath.WalkDir(sourceDirPath, func(path string, d fs.DirEntry, err error) error {
// Error with path.
if err != nil {
return err
}
// Skip the source directory root. This can be ignored because the source
// directory root is just the root of the zip file.
if sourceDir == path {
return nil
}
// Skip directories. Directories will be created automatically from paths to
// each file to zip up.
if d.IsDir() {
return nil
}
// Handle formatting path name properly for use in zip file. Paths must be
// relative, not start with a slash character, and must use forward slashes,
// even on Windows. See: https://pkg.go.dev/archive/zip#Writer.Create
//
// Directories are created automatically based on the subdirectories provided
// in each file's path.
zipPath := strings.Replace(path, sourceDir, "", 1)
zipPath = strings.TrimPrefix(zipPath, string(filepath.Separator))
zipPath = filepath.ToSlash(zipPath)
// Open the path to read from.
f, err := os.Open(path)
if err != nil {
return err
}
defer f.Close()
// Create the path in the zip.
w, err := z.Create(zipPath)
if err != nil {
return err
}
// Write the source file into the zip at path from Create().
_, err = io.Copy(w, f)
if err != nil {
return err
}
return nil
})
if err != nil {
return
}
// Zip file created.
err = z.Close()
if err != nil {
return
}
return
}
Zipping a directory is unclear and slightly confusing via the
archive/zippackage, mostly due to the way paths need to be handled and how directories are created. I suggest adding a func to handle zipping a directory simply to aid in implementation. I have had to implement, or educate others on implementing, similar code for various projects over the years. Furthermore, help via StackOverflow, golang-nuts, etc., isn't very clear either. There is clearly somewhat wide-spread confusion regarding zipping a directory.Pseudo-code is below.