从 Go 中的 tar 文件中提取

Extract from tar file in Go

此代码尝试将一些文本 tar 放入 tar 文件中并取消 tar 它。 tar 的代码有效,但似乎我做错了什么 因为 untar 同一个文件不起作用。

当我使用OS GUI 手动tar.gz 取消tar 文件时,它可以工作但是 不在此代码中。

http://play.golang.org/p/diTOojUuBX

func main() {
        mpath := "a.tar.gz"
        // defer os.Remove(mpath)
        f, err := overwrite(mpath)
        defer f.Close()
        if err != nil {
                panic(err)
        }
        gw := gzip.NewWriter(f)
        defer gw.Close()
        if err != nil {
                panic(err)
        }
        tw := tar.NewWriter(gw)
        for _, file := range files {
                hdr := &tar.Header{
                        Name: file.Name,
                        Mode: 0600,
                        Size: int64(len(file.Body)),
                }
                if err := tw.WriteHeader(hdr); err != nil {
                        panic(err)
                }
                if _, err := tw.Write([]byte(file.Body)); err != nil {
                        panic(err)
                }
        }
        // Make sure to check the error on Close.
        if err := tw.Close(); err != nil {
                panic(err)
        }

        fr, err := read(mpath)
        defer fr.Close()
        if err != nil {
                panic(err)
        }
        gr, err := gzip.NewReader(fr)
        defer gr.Close()
        if err != nil {
                panic(err)
        }
        tr := tar.NewReader(gr)
        for {
                hdr, err := tr.Next()
                if err == io.EOF {
                        // end of tar archive
                        break
                }
                if err != nil {
                        panic(err)
                }
                path := hdr.Name
                switch hdr.Typeflag {
                case tar.TypeDir:
                        if err := os.MkdirAll(path, os.FileMode(hdr.Mode)); err != nil {
                                panic(err)
                        }
                case tar.TypeReg:
                        ow, err := overwrite(path)
                        defer ow.Close()
                        if err != nil {
                                panic(err)
                        }
                        if _, err := io.Copy(ow, tr); err != nil {
                                panic(err)
                        }
                default:
                        fmt.Printf("Can't: %c, %s\n", hdr.Typeflag, path)
                }
        }
}

在我看来有两个问题。

  1. 您正在使用 defer 来关闭您的 tar writer 和 gzip writer,但是 defer 仅在当前作用域结束时执行。由于您是 运行 这一切都在一个函数中,当您尝试读取 untar 时,您的文件句柄仍将打开,这可能会导致问题(例如,文件可能没有完全刷新)。

  2. 创建 tar 球时,您没有在 header 中设置 Typeflag。虽然 GNU tar 可以处理这个问题,但假设 Typeflag 为“0”,Go 可能不会。根据文档 http://www.gnu.org/software/tar/manual/html_node/Standard.html,常规文件的 Typeflag 是字节“0”。这可能意味着您需要为代码中的每个资源(目录、文件、链接等)设置 Typeflag。

我重写了你的代码如下,现在对我有用了。 (注意:将所有内容存储为 REGTYPE)

http://play.golang.org/p/3B7F_axr-i

编辑
Ah-ha,我现在知道#2 的问题了。 tar 的 Go 库使用 FileInfoHeader 来确定 header 的部分,例如 Typeflag。由于您的 文件 不是系统上的真正文件,因此无法填写适当的类型标志。

GNU tar 显然知道如何处理这个问题,或者可能会尽力解决并在这种情况下成功。