从 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)
}
}
}
在我看来有两个问题。
您正在使用 defer 来关闭您的 tar writer 和 gzip writer,但是 defer 仅在当前作用域结束时执行。由于您是 运行 这一切都在一个函数中,当您尝试读取 untar 时,您的文件句柄仍将打开,这可能会导致问题(例如,文件可能没有完全刷新)。
创建 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 显然知道如何处理这个问题,或者可能会尽力解决并在这种情况下成功。
此代码尝试将一些文本 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)
}
}
}
在我看来有两个问题。
您正在使用 defer 来关闭您的 tar writer 和 gzip writer,但是 defer 仅在当前作用域结束时执行。由于您是 运行 这一切都在一个函数中,当您尝试读取 untar 时,您的文件句柄仍将打开,这可能会导致问题(例如,文件可能没有完全刷新)。
创建 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 显然知道如何处理这个问题,或者可能会尽力解决并在这种情况下成功。