如何在Golang中将一个目录(不仅仅是其中的文件)写入tar.gz文件

huangapple go评论93阅读模式
英文:

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

问题

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")
}

英文:

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.

<br> 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 (
      &quot;fmt&quot;
      &quot;os&quot;
      &quot;io&quot;
      &quot;log&quot;
      &quot;strings&quot;
      &quot;archive/tar&quot;
      &quot;compress/gzip&quot;
    )
    
    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 + &quot;/&quot; + fi.Name()
        if fi.IsDir() {
          //TarGzWrite( curPath, tw, fi )
          IterDirectory( curPath, tw )
        } else {
          fmt.Printf( &quot;adding... %s\n&quot;, 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( &quot;tar.gz ok&quot; )
    }
    
    func main() {
      targetFilePath := &quot;test.tar.gz&quot;
      inputDirPath := &quot;test/&quot;
      TarGz( targetFilePath, strings.TrimRight( inputDirPath, &quot;/&quot; ) )
      fmt.Println( &quot;Hello, World&quot; )
    }

答案1

得分: 13

你只是将文件名添加到tar中,而不是整个路径。你需要保留整个路径,以便Tar能够理解目录。你只需要更改一行代码:

h.Name = fi.Name()

应该改为:

h.Name = _path

在Linux上,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
英文:

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

答案2

得分: 6

另一种方法是使用内置的filepath.Walk函数

// root_directory已在上面设置好

walkFn := func(path string, info os.FileInfo, err error) error {
    if info.Mode().IsDir() {
        return nil
    }
    // 由于作用域的原因,我们可以引用外部的root_directory变量
    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
}
英文:

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
}

huangapple
  • 本文由 发表于 2012年11月29日 01:36:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/13611100.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定