package archive import ( "archive/tar" "compress/gzip" "errors" "fmt" "io" "os" "path/filepath" "strings" ) // TarGzDir creates a tar.gz archive and returns it's path. // abosolutePath should be an absolute path to a directory. // Archive name will be .tar.gz and will be placed next to the directory. func TarGzDir(absolutePath string) (string, error) { targzPath := filepath.Join(absolutePath, fmt.Sprintf("%s.tar.gz", filepath.Base(absolutePath))) outFile, err := os.Create(targzPath) if err != nil { return "", err } defer outFile.Close() zipWriter := gzip.NewWriter(outFile) defer zipWriter.Close() tarWriter := tar.NewWriter(zipWriter) defer tarWriter.Close() err = filepath.Walk(absolutePath, func(path string, info os.FileInfo, err error) error { if err != nil { return err } if path == targzPath { return nil // skip archive file } pathInArchive := filepath.Clean(strings.TrimPrefix(path, absolutePath)) if pathInArchive == "" { return nil // skip root dir } return addToArchive(tarWriter, pathInArchive, path, info) }) return targzPath, err } func addToArchive(tarWriter *tar.Writer, pathInArchive string, path string, info os.FileInfo) error { if info.IsDir() { return nil } file, err := os.Open(path) if err != nil { return err } stat, err := file.Stat() if err != nil { return err } header, err := tar.FileInfoHeader(stat, stat.Name()) if err != nil { return err } header.Name = pathInArchive // use relative paths in archive err = tarWriter.WriteHeader(header) if err != nil { return err } if stat.IsDir() { return nil } _, err = io.Copy(tarWriter, file) return err } // ExtractTarGz reads a .tar.gz archive from the reader and extracts it into outputDirPath directory func ExtractTarGz(r io.Reader, outputDirPath string) error { zipReader, err := gzip.NewReader(r) if err != nil { return err } defer zipReader.Close() tarReader := tar.NewReader(zipReader) for { header, err := tarReader.Next() if errors.Is(err, io.EOF) { break } if err != nil { return err } switch header.Typeflag { case tar.TypeDir: // skip, dir will be created with a file case tar.TypeReg: p := filepath.Clean(filepath.Join(outputDirPath, header.Name)) if err := os.MkdirAll(filepath.Dir(p), 0o744); err != nil { return fmt.Errorf("Failed to extract dir %s", filepath.Dir(p)) } outFile, err := os.Create(p) if err != nil { return fmt.Errorf("Failed to create file %s", header.Name) } if _, err := io.Copy(outFile, tarReader); err != nil { return fmt.Errorf("Failed to extract file %s", header.Name) } outFile.Close() default: return fmt.Errorf("tar: unknown type: %v in %s", header.Typeflag, header.Name) } } return nil }