Created
March 13, 2020 20:14
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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