package archive import ( "archive/tar" "path/filepath" "compress/gzip" "io" "os" "strings" ) func TarToFile(Output string, Input []string, compressionType string) error { var err error file, err := os.Create(Output) if err != nil { return err } defer file.Close() return Tar(file, Input, compressionType) } func Tar(writer io.Writer, Input []string, compressionType string) error { var twriter *tar.Writer // Set the compression type... if any switch compressionType { case "gz": gzwriter, err := gzip.NewWriterLevel(writer, gzip.BestSpeed) if err != nil { return err } defer gzwriter.Close() // Write to the gzip writer twriter = tar.NewWriter(gzwriter) default: // Write directly to the io.Writer twriter = tar.NewWriter(writer) } // Close off the tar writer when we're done defer twriter.Close() for _, v := range 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() // Convert Windows paths to Unix paths path = strings.Replace(path, "\\", "/", -1) // TODO; See if tar.FileInfoheader() could be used instead // without the pathing issues I encountered h := &tar.Header{ Name: path, Size: info.Size(), // I don't like it... but it helps with platform compatibility Mode: 0664, 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 } }