I want to write a tar_gz tool in Go. The input is just like linux command:
$tar czvf targetFileName inputDirectoryPath
Suppose I have an inputDirectory structured as below:
test [dir]
-- 0.txt
-- 1 [sub dir]
-- 1.txt
For Example: use command:
$tar czvf test.tar.gz test/
we can tar and gzip the whole test directory.
My Problem is I can write a tar and gz route to recursively iterate all the file in test directory and write the file to test.tar.gz file. But I don't know how to write a directory to the test.tar.gz. After running my program, the structure in test.tar.gz file is:
0.txt
1.txt
Can anyone tell me how to write the directory recursively to the output tar.gz file. Thanks a lot.
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" )
}
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
}
You're only adding the filename to the tar, not the entire path. You need to keep the whole path for Tar to be able to understand directories. You just need to change one line:
h.Name = fi.Name()
Should 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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With