Extract from tar file in Go

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

Extract from tar file in Go

问题

这段代码尝试将一些文本打包成一个 tar 文件,并解压缩它。
tar 部分的代码是有效的,但似乎我做错了一些事情,因为使用相同的文件进行解压缩不起作用。

当我使用操作系统图形界面手动打包成 tar.gz 文件并解压缩时,它可以正常工作,但在这段代码中不行。

以下是代码的翻译:

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)
        }
    }
    // 确保在关闭之前检查错误。
    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 {
            // tar 归档结束
            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("无法处理: %c, %s\n", hdr.Typeflag, path)
        }
    }
}

希望对你有帮助!如果你有任何其他问题,请随时问我。

英文:

This code tries to tar some texts into a tar file and untar it.
The code for tar works but seems like I am doing something wrong
because untar the same file does not work.

When I untar the file that I manually tar.gz with OS GUI, it works but
not in this code.

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

得分: 3

我觉得有两个问题。

  1. 你在使用defer关闭tar writer和gzip writer时,但是defer只在当前作用域结束时执行。由于你在一个函数中运行所有操作,当你尝试读取解压缩时,文件句柄仍然是打开状态,这可能会导致问题(例如,文件可能没有完全刷新)。

  2. 在创建tarball时,你没有设置头部的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

编辑
啊哈,我现在知道问题2的原因了。Go的tar库使用FileInfoHeader来确定头部的一些部分,比如Typeflag。由于你的文件实际上不是系统上的真实文件,它无法填写适当的Typeflag。

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

英文:

It looks to me like there are two problems.

  1. You are using defer to close your tar writer and gzip writer, however defers only execute when the current scope ends. Since you are running this all in one function your file handles will still be open when you attempt to read to untar and that may cause issues (perhaps file has not flushed fully for example).

  2. You are not setting the Typeflag in the header when you create the tarball. While GNU tar may deal with this, assuming Typeflag '0', Go may not. According to the docs http://www.gnu.org/software/tar/manual/html_node/Standard.html the Typeflag for a regular file is byte '0'. This may mean you need to set the Typeflag on a per resource in your code (directories, files, links, etc).

I rewrote you code like the following, and it is working for me now. (note: storing everything as REGTYPE)

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

EDIT
Ah-ha, I know the problem with #2 now. The Go library for tar uses FileInfoHeader in order to determine parts of the header, like Typeflag. Since your files are not really files on the system it cannot fill out the appropriate Typeflag.

GNU tar obviously knows how to deal with this, or perhaps tries its best to figure it out and is successful in this case.

huangapple
  • 本文由 发表于 2015年8月28日 00:21:12
  • 转载请务必保留本文链接:https://go.coder-hub.com/32254757.html
匿名

发表评论

匿名网友

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

确定