| package zip | 
 |  | 
 | import ( | 
 | 	"archive/zip" | 
 | 	"fmt" | 
 | 	"io" | 
 | 	"os" | 
 | 	"path/filepath" | 
 | ) | 
 |  | 
 | func forZipFile(zipRC *zip.ReadCloser, fn func(f *zip.File, r io.Reader) error) error { | 
 | 	for _, f := range zipRC.File { | 
 | 		rc, err := f.Open() | 
 | 		if err != nil { | 
 | 			return err | 
 | 		} | 
 | 		if err := fn(f, rc); err != nil { | 
 | 			_ = rc.Close() | 
 | 			return err | 
 | 		} | 
 | 		if err := rc.Close(); err != nil { | 
 | 			return err | 
 | 		} | 
 | 	} | 
 | 	return nil | 
 | } | 
 |  | 
 | // UnZip unzips the file specified in src into the 'dest' directory. | 
 | // Note: | 
 | // * The parent directory will be created using the mode of the first directory in the zip. | 
 | // * src could be partially populated if an error is returned. | 
 | func UnZip(dest, src string) error { | 
 | 	r, err := zip.OpenReader(src) | 
 | 	if err != nil { | 
 | 		return err | 
 | 	} | 
 |  | 
 | 	fn := func(f *zip.File, r io.Reader) error { | 
 | 		path := filepath.Join(dest, f.Name) | 
 | 		if f.FileInfo().IsDir() { | 
 | 			if err := os.MkdirAll(path, f.Mode()); err != nil { | 
 | 				return err | 
 | 			} | 
 | 		} else { | 
 | 			contents, err := io.ReadAll(r) | 
 | 			if err != nil { | 
 | 				return err | 
 | 			} | 
 | 			return os.WriteFile(path, contents, f.Mode()) | 
 | 		} | 
 | 		return nil | 
 | 	} | 
 |  | 
 | 	if err := forZipFile(r, fn); err != nil { | 
 | 		_ = r.Close() | 
 | 		return err | 
 | 	} | 
 |  | 
 | 	return r.Close() | 
 | } | 
 |  | 
 | // Directory zips the specified directory into the target file. | 
 | // Note: source must be an absolute path. Also, this is untested with symlinks. | 
 | func Directory(target, source string) error { | 
 | 	zipfile, err := os.Create(target) | 
 | 	if err != nil { | 
 | 		return err | 
 | 	} | 
 |  | 
 | 	archive := zip.NewWriter(zipfile) | 
 |  | 
 | 	walkErr := 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 | 
 | 		} | 
 |  | 
 | 		header.Name, err = filepath.Rel(source, path) | 
 | 		if err != nil { | 
 | 			return err | 
 | 		} | 
 |  | 
 | 		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 | 
 | 		} | 
 | 		if _, err = io.Copy(writer, file); err != nil { | 
 | 			_ = file.Close() | 
 | 			return err | 
 | 		} | 
 | 		if err := file.Close(); err != nil { | 
 | 			return fmt.Errorf("Failed to close file %s: %s", file.Name(), err) | 
 | 		} | 
 | 		return nil | 
 | 	}) | 
 |  | 
 | 	if err := archive.Close(); err != nil { | 
 | 		_ = zipfile.Close() | 
 | 		return fmt.Errorf("Failed to close writer to zipfile %s: %s : %v", zipfile.Name(), err, walkErr) | 
 | 	} | 
 | 	if err := zipfile.Close(); err != nil { | 
 | 		return fmt.Errorf("Failed to close the zipfile %s: %s : %v", zipfile.Name(), err, walkErr) | 
 | 	} | 
 |  | 
 | 	return walkErr | 
 | } |