我正在尝试使用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问题,您需要显示您想要完成的工作,而不是您认为需要做的事情。