使用golang io.pipe到tar文件的错误

问题描述:

我一直在使用io.Pipe测试代码,将tar和gunzip文件转换成tar球,然后使用tar实用程序进行解压缩。后续的代码通过,但是untaring过程中不断变得使用golang io.pipe到tar文件的错误

错误: tar: Truncated input file (needed 1050624 bytes, only 0 available) tar: Error exit delayed from previous errors.

这个问题真的快把我逼疯了。已经两周了。我真的需要帮助调试。

谢谢。

开发环境:去版本go1.9达尔文/ AMD64

package main 

import (
    "archive/tar" 
    "compress/gzip" 
    "fmt" 
    "io" 
    "log" 
    "os" 
    "path/filepath" 
    "testing" 
) 

func testTarGzipPipe2(t *testing.T) { 
    src := "/path/to/file/folder" 

    pr, pw := io.Pipe() 
    gzipWriter := gzip.NewWriter(pw) 
    defer gzipWriter.Close() 
    tarWriter := tar.NewWriter(gzipWriter) 
    defer tarWriter.Close() 

    status := make(chan bool) 

    go func() { 
     defer pr.Close() 
     // tar to local disk 
     tarFile, err := os.OpenFile("/path/to/tar/ball/test.tar.gz", os.O_RDWR|os.O_CREATE, 0755) 
     if err != nil { 
      log.Fatal(err) 
     } 
     defer tarFile.Close() 
     if _, err := io.Copy(tarFile, pr); err != nil { 
      log.Fatal(err) 
     } 

     status <- true 
    }() 

    err := filepath.Walk(src, func(path string, info os.FileInfo, err error) error { 
     if err != nil { 
      return err 
     } 

     header, err := tar.FileInfoHeader(info, info.Name()) 
     if err != nil { 
      return err 
     } 

     // header.Name = strings.TrimPrefix(strings.Replace(path, src, "", -1), string(filepath.Separator)) 

     if err := tarWriter.WriteHeader(header); err != nil { 
      return err 
     } 

     if info.Mode().IsDir() { 
      return nil 
     } 

     fmt.Println(path) 
     f, err := os.Open(path) 
     if err != nil { 
      return err 
     } 
     defer f.Close() 

     if _, err := io.Copy(tarWriter, f); err != nil { 
      return err 
     } 

     return nil 
    }) 

    if err != nil { 
     log.Fatal(err) 
    } 

    pw.Close() 
    <-status 
} 
+2

什么是错误?还请'去fmt'文件 – reticentroot

+0

不相关:你不应该在测试中(或者你期望执行'defer'语句的任何地方)调用log.Fatal,因为它阻止了你打算做的任何清理,并且可以隐藏未通过测试。有一个't.Fatal'这是你想要的。一个tar文件不应该是可执行的。关闭一个通道而不是发送一个标记值作为信号,或者更好的是,使用WaitGroup来等待goroutines。 – JimB

gzipWritertarWriter递延关闭呼叫之前,您正在关闭管道。没有错误,因为您没有检查这两个关闭呼叫中的错误。您需要依次关闭tarWriter,然后关闭gzipWriter,然后关闭PipeWriter

但是,在这段代码中根本没有理由使用pipe,如果直接写入文件,可以删除goroutine和相关的协调。

tarFile, err := os.OpenFile("/tmp/test.tar.gz", os.O_RDWR|os.O_CREATE, 0644) 
if err != nil { 
    log.Fatal(err) 
} 
defer tarFile.Close() 

gzipWriter := gzip.NewWriter(tarFile) 
defer gzipWriter.Close() 
tarWriter := tar.NewWriter(gzipWriter) 
defer tarWriter.Close() 
+0

我忘记的一件重要事情是以相反的顺序关闭打开的作者。希望别人能从我的错误中吸取教训。再次感谢@ JimB。 – NSTNF

+0

@NSTNF:实际上你有'tarWriter.Close()'和'gzipWriter.Close()'正确,延迟调用按LIFO顺序执行。 – JimB

+0

谢谢。 Golang与其他langs不同。它要求开发人员改变编码思想。我努力正确编码,然后高效地进行编码。打开的作家需要以相反的顺序关闭。这可能是一个常见的错误。如果创建一个常见的错误wiki或者在关闭gzip writer之前添加诸如“warning:关闭tar作家”之类的东西给相应的golang文档,会对其他开发者有利吗? – NSTNF