我正在尝试使用golang中的io.Pipe()函数流式处理zip文件的字节。 我正在使用管道读取器来读取zip中每个文件的字节,然后将其流式传输并使用管道写入器将字节写入响应对象中。

1
2
3
4
5
6
7
8
9
10
11
func main() {
  r, w := io.Pipe()
 // go routine to make the write/read non-blocking
  go func() {

    defer w.Close()

    bytes, err := ReadBytesforEachFileFromTheZip()
    err := json.NewEncoder(w).Encode(bytes)
    handleErr(err)
  }()

这不是可行的实现,而是我要实现的结构。 我不想使用ioutil.ReadAll,因为文件将非常大,而Pipe()将帮助我避免将所有数据都带入内存。 有人可以使用io.Pipe()帮助工作实现吗?

  • 您需要读取整个文件。 zip.NewReader取一个io.ReaderAt,您不能用Pipe做。
  • 无法使用io.Copy(zipReader,w)? golang.org/pkg/io/#Copy
  • @ mh-cbon我不确定100%但不是io.Copy在下面使用缓冲区吗?我想避免这种情况。
  • @JimB有没有办法避免以某种方式读取整个zip文件?我敢肯定,当zip文件很大时,没有人愿意将所有数据带入内存。如何读取块中的字节?很好奇,如果它是常规目录,我可以使用Pipe吗?
  • @psbits:这就是zip格式的工作方式。中央目录位于文件的末尾,因此您不能(轻松或正确)提取文件,而无需先寻找文件的末尾并读取目录。
  • 是的,有用于操作的缓冲区。无论如何,我都仔细检查过,是的zipReader本身不是Reader,所以即使io.copy也无法使用。您必须仔细阅读每个文件才能获得阅读器。
  • @ mh-cbonYup,我正在尝试...循环遍历zip中的每个文件并读取其字节:zf,err:= zip.OpenReader(" test.zip")表示_,文件:=范围zf.File { fc,err:= file.Open()//读取字节并用管道传输?? } jimB提到我们不能在此处使用Pipe读取字节并将其传递给writer?

我使用golang io.Pipe()使其工作。Pipewriter将字节以块的形式写入管道,并从另一端将pipeReader读取器写入。 使用go例程的原因是要进行无阻塞的写操作,同时从管道进行同时读取。

注意:关闭管道编写器(w.Close())以在流上发送EOF很重要,否则它将不会关闭流。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
func DownloadZip() ([]byte, error) {
    r, w := io.Pipe()

    defer r.Close()
    defer w.Close()

    zip, err := os.Stat("temp.zip")
    if err != nil{
        return nil, err
    }

    go func(){

        f, err := os.Open(zip.Name())
        if err != nil {
            return

        }

        buf := make([]byte, 1024)
        for {
            chunk, err := f.Read(buf)
            if err != nil && err != io.EOF {
                panic(err)
            }
            if chunk == 0 {
                break
            }

            if _, err := w.Write(buf[:chunk]); err != nil{
                return
            }

        }

        w.Close()
    }()

    body, err := ioutil.ReadAll(r)
    if err != nil {
        return nil, err
    }
    return body, nil

}

请让我知道是否有人有其他方法。

  • goroutine和管道在这里毫无意义。您仍在同步读取文件,只是无缘无故地添加了另一个副本。
  • @JimB您能解释一下灌浆/管道在这里毫无意义吗? go例程使管道编写器以块的形式写入管道,而不会阻塞从另一端读取的管道读取器。我确实同意它的一种副本,但是那会不会一次复制所有数据?可能我可以使用copyN。
  • 任何读取/复制都必须分块进行,因此我不确定"一次全部"的含义。您在这里拥有的是io.Copy的低效率版本,但是由于ioutil.ReadAll会将整个文件读入内存中,因此根本不需要复制,因此直接使用它即可。
  • 这就说得通了。我试图使用io.Pipe()实现的目的是将数据块写入管道并从另一端读取它,因此我不必将所有数据都放到内存中。有什么办法可以实现@jimB吗?
  • 管道无关紧要。您使用管道将需要io.Reader的对象与需要io.Writer的对象同步连接。就这些。一次或一次不将整个文件读取到内存完全取决于您-即,如果您不想读取所有文件,请不要使用ReadAll。您似乎在这里遇到XY问题,您需要显示您想要完成的工作,而不是您认为需要做的事情。