package archive import ( "archive/tar" "path/filepath" "compress/gzip" "io" "os" ) type Archive struct { Output string Input []string file *os.File } func (a *Archive) Tar(compressionType string) error { var err error if a.file, err = os.Create(a.Output); err != nil { return err } defer a.file.Close() var twriter *tar.Writer // Set the compression type... if any switch compressionType { case "gz": gzwriter, err := gzip.NewWriterLevel(a.file, gzip.BestSpeed) if err != nil { return err } defer gzwriter.Close() // Write to the gzip writer twriter = tar.NewWriter(gzwriter) default: // Write directly to the file twriter = tar.NewWriter(a.file) } // Close off the tar writer when we're done defer twriter.Close() for _, v := range a.Input { if err := filepath.Walk(v, tarWalkfn(twriter)); err != nil { return err } } return nil } func tarWalkfn(writer *tar.Writer) filepath.WalkFunc { // This is an interesting trick to get around scoping issues return func(path string, info os.FileInfo, err error) error { if err != nil { return err } if info.IsDir() { return nil } f, err := os.Open(path) if err != nil { return err } defer f.Close() // TODO; See if tar.FileInfoheader() could be used instead // without the pathing issues I encountered h := &tar.Header{ Name: path, Size: info.Size(), Mode: int64(info.Mode()), ModTime: info.ModTime(), } err = writer.WriteHeader(h) if err != nil { return err } _, err = io.Copy(writer, f) if err != nil { // TODO: Figure out how to add more useful information to // These errors // fmt.Fprintln(os.Stderr, f.Name()) return err } return nil } }