Skip to content

Instantly share code, notes, and snippets.

@traetox
Created March 13, 2020 20:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save traetox/348cb9684cb1ecaba2319283af65e6b8 to your computer and use it in GitHub Desktop.
Save traetox/348cb9684cb1ecaba2319283af65e6b8 to your computer and use it in GitHub Desktop.
Simple golang program to merge multiple tar files into a single tar file using parallel compression
package main
import (
"archive/tar"
"flag"
"fmt"
"io"
"log"
"net/http"
"os"
"github.com/klauspost/pgzip"
)
const (
gzipCT string = `application/x-gzip`
tarCT string = `application/octet-stream` //an assumption
)
var (
outF = flag.String("o", "", "Output file")
compF = flag.Bool("c", true, "Compress output")
concF = flag.Int("p", 0, "Override concurrency")
)
func main() {
var out io.Writer
flag.Parse()
args := flag.Args()
if err := checkPaths(args); err != nil {
log.Fatal(err)
}
if *outF == `` {
log.Fatal("Missing output file")
} else if *outF == `-` {
out = os.Stdout
} else {
if fout, err := os.Create(*outF); err != nil {
log.Fatal("Failed to open", *outF, err)
} else {
defer fout.Close()
out = fout
}
}
var cout *pgzip.Writer
if *compF {
cout = pgzip.NewWriter(out)
out = cout
}
tw := tar.NewWriter(out)
for _, v := range args {
if err := addTar(tw, v); err != nil {
log.Fatalf("Failed to merge %s: %v\n", v, err)
}
}
if err := tw.Close(); err != nil {
log.Fatalf("Failed to close tar writer %v\n", err)
}
if cout != nil {
if err := cout.Flush(); err != nil {
log.Fatal("Failed to flush", err)
} else if err = cout.Close(); err != nil {
log.Fatal("Failed to close compressed writer", err)
}
}
}
func addTar(tw *tar.Writer, pth string) (err error) {
var tr *tar.Reader
var rc io.ReadCloser
var hdr *tar.Header
if tr, rc, err = openTarFile(pth); err != nil {
return
}
for {
if hdr, err = tr.Next(); err != nil {
if err == io.EOF {
err = nil
}
break
}
if err = tw.WriteHeader(hdr); err != nil {
break
} else if _, err = io.Copy(tw, tr); err != nil {
break
}
}
if err == nil {
err = rc.Close()
} else {
rc.Close()
}
return
}
func checkPaths(pths []string) (err error) {
var rdr io.ReadCloser
for _, v := range pths {
if _, rdr, err = openTarFile(v); err != nil {
break
}
if err = rdr.Close(); err != nil {
break
}
}
return
}
func openTarFile(pth string) (tr *tar.Reader, rc io.ReadCloser, err error) {
var fin *os.File
var n int
buff := make([]byte, 1024)
if fin, err = os.Open(pth); err != nil {
return
}
if n, err = fin.Read(buff); err != nil {
fin.Close()
return
} else if n == 0 {
fin.Close()
err = fmt.Errorf("%s is empty", pth)
return
}
ct := http.DetectContentType(buff[0:n])
if _, err = fin.Seek(0, 0); err != nil {
fin.Close()
return
}
switch ct {
case gzipCT:
//gzip, so assume we are good
if rc, err = newCrdr(fin); err != nil {
fin.Close()
err = fmt.Errorf("Error opening %s %v", pth, err)
return
}
case tarCT:
rc = fin
default:
fin.Close()
err = fmt.Errorf("%s is an unknown file type", pth)
return
}
tr = tar.NewReader(rc)
return
}
func newCrdr(fin *os.File) (c *crdr, err error) {
c = &crdr{
fin: fin,
}
c.Reader, err = pgzip.NewReader(fin)
return
}
type crdr struct {
*pgzip.Reader
fin *os.File
}
func (c *crdr) Close() (err error) {
if err = c.Reader.Close(); err == nil {
err = c.fin.Close()
} else {
c.fin.Close()
}
return
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment