How to write a directory [and not just files in it] to a tar.gz file in golang

I want to write the tar_gz tool in Go. The input is similar to the linux command:

$tar czvf targetFileName inputDirectoryPath 

Suppose I have an inputDirectory structured as follows:

 test [dir] -- 0.txt -- 1 [sub dir] -- 1.txt 

Example: use the command:

 $tar czvf test.tar.gz test/ 

we can tar and gzip the entire test directory.


My problem: I can write the tar and gz route to recursively iterate over the entire file in the test directory and write the file to the test.tar.gz file. But I do not know how to write the test.tar.gz directory. After starting my program, the structure in the test.tar.gz file is:

 0.txt 1.txt 

Can someone tell me how to write the directory recursively to the tar.gz output file. Many thanks.

  package main import ( "fmt" "os" "io" "log" "strings" "archive/tar" "compress/gzip" ) func handleError( _e error ) { if _e != nil { log.Fatal( _e ) } } func TarGzWrite( _path string, tw *tar.Writer, fi os.FileInfo ) { fr, err := os.Open( _path ) handleError( err ) defer fr.Close() h := new( tar.Header ) h.Name = fi.Name() h.Size = fi.Size() h.Mode = int64( fi.Mode() ) h.ModTime = fi.ModTime() err = tw.WriteHeader( h ) handleError( err ) _, err = io.Copy( tw, fr ) handleError( err ) } func IterDirectory( dirPath string, tw *tar.Writer ) { dir, err := os.Open( dirPath ) handleError( err ) defer dir.Close() fis, err := dir.Readdir( 0 ) handleError( err ) for _, fi := range fis { curPath := dirPath + "/" + fi.Name() if fi.IsDir() { //TarGzWrite( curPath, tw, fi ) IterDirectory( curPath, tw ) } else { fmt.Printf( "adding... %s\n", curPath ) TarGzWrite( curPath, tw, fi ) } } } func TarGz( outFilePath string, inPath string ) { // file write fw, err := os.Create( outFilePath ) handleError( err ) defer fw.Close() // gzip write gw := gzip.NewWriter( fw ) defer gw.Close() // tar write tw := tar.NewWriter( gw ) defer tw.Close() IterDirectory( inPath, tw ) fmt.Println( "tar.gz ok" ) } func main() { targetFilePath := "test.tar.gz" inputDirPath := "test/" TarGz( targetFilePath, strings.TrimRight( inputDirPath, "/" ) ) fmt.Println( "Hello, World" ) } 
+7
source share
2 answers

You add only the file name to tar, not the whole path. You need to save all the way for tar to understand directories. You just need to change one line:

 h.Name = fi.Name() 

Must be:

 h.Name = _path 

On Linux, the output of tar -tvf test.tar.gz :

 -rw-rw-r-- 0/0 0 2012-11-28 11:17 test/0.txt -rw-rw-r-- 0/0 0 2012-11-28 11:17 test/sub/1.txt 
+10
source

An alternative is to use the built-in filepath.Walk function

 // root_directory has been set further up walkFn := func(path string, info os.FileInfo, err error) error { if info.Mode().IsDir() { return nil } // Because of scoping we can reference the external root_directory variable new_path := path[len(root_directory):] if len(new_path) == 0 { return nil } fr, err := os.Open(path) if err != nil { return err } defer fr.Close() if h, err := tar.FileInfoHeader(info, new_path); err != nil { log.Fatalln(err) } else { h.Name = new_path if err = tw.WriteHeader(h); err != nil { log.Fatalln(err) } } if length, err := io.Copy( tw, fr ); err != nil { log.Fatalln(err) } else { fmt.Println(length) } return nil } if err = filepath.Walk(root_directory, walkFn); err != nil { return err } 
+4
source

All Articles