2012-11-28 8 views
7

Chcę napisać narzędzie tar_gz w Go. Wejście jest jak polecenia Linux:jak napisać katalog [nie tylko pliki w nim] do pliku tar.gz w golang

$tar czvf targetFileName inputDirectoryPath 

Załóżmy Mam inputDirectory skonstruowane jak poniżej:

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

Dla przykładu: użyj polecenia:

$tar czvf test.tar.gz test/ 

możemy tar i gzip cały katalog testowy.


Mój problem polega na tym, że mogę napisać trasę tar i gz, aby rekurencyjnie powtórzyć cały plik w katalogu testowym i napisać plik do pliku test.tar.gz. Ale nie wiem, jak napisać katalog do test.tar.gz. Po uruchomieniu mój program, struktura pliku jest w test.tar.gz:

0.txt 
1.txt 

Czy ktoś może mi powiedzieć jak napisać katalog rekurencyjnie do pliku wyjściowego tar.gz. Wielkie dzięki.

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

Odpowiedz

10

Dodajesz nazwę pliku do tar, a nie całą ścieżkę. Trzeba utrzymywać całą ścieżkę, aby Tar mógł zrozumieć katalogi. Wystarczy zmienić jedną linię:

h.Name = fi.Name() 

powinno być:

h.Name = _path 

na Linuksie, wyjście 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 
+0

Niż dużo ks. To działa. – MadCrazy

4

Alternatywą jest użycie wbudowanego w filePath. Funkcja spaceru

// 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 
} 
+1

Myślę, że 'new_path: = ścieżka [len (root_directory) +1:]' lepiej unikać prefiksu każdego pliku. – DarKnight