我正在努力处理Go中的嵌套zip文件(其中zip文件包含另一个zip文件)。我正在尝试递归一个zip文件并列出它包含的所有文件。
archive / zip为您提供了两种处理zip文件的方法:
OpenReader
在磁盘上打开一个文件。 NewReader
接受io.ReaderAt
和文件大小。当您使用其中任何一个迭代压缩文件时,您会为zip中的每个文件获得zip.File
。要获取文件f的文件内容,请致电f.Open
,它会为您提供zip.ReadCloser
。要打开嵌套的zip文件,我需要使用NewReader
,但zip.File
和zip.ReadCloser
不满足io.ReaderAt
界面。
zip.File
有一个私人字段zipr
,其中io.ReaderAt
和zip.ReadCloser
的私有字段f
是os.File
,应该NewReader
满足ModelMultipleChoiceField
。
我的问题:有没有办法打开嵌套的zip文件,而无需先将内容写入磁盘上的文件,或将整个内容读入内存。
看起来所需的一切都在zip.File中可用,但不会导出。我希望我错过了什么。
答案 0 :(得分:2)
如果你决定倒退,那么io.ReaderAt
io.Reader
重新初始化的情况如何:(这段代码很大程度上没有经过测试,但希望你能得到这个想法)
package main
import (
"io"
"io/ioutil"
"os"
"strings"
)
type inefficientReaderAt struct {
rdr io.ReadCloser
cur int64
initer func() (io.ReadCloser, error)
}
func newInefficentReaderAt(initer func() (io.ReadCloser, error)) *inefficientReaderAt {
return &inefficientReaderAt{
initer: initer,
}
}
func (r *inefficientReaderAt) Read(p []byte) (n int, err error) {
n, err = r.rdr.Read(p)
r.cur += int64(n)
return n, err
}
func (r *inefficientReaderAt) ReadAt(p []byte, off int64) (n int, err error) {
// reset on rewind
if off < r.cur || r.rdr == nil {
r.cur = 0
r.rdr, err = r.initer()
if err != nil {
return 0, err
}
}
if off > r.cur {
sz, err := io.CopyN(ioutil.Discard, r.rdr, off-r.cur)
n = int(sz)
if err != nil {
return n, err
}
}
return r.Read(p)
}
func main() {
r := newInefficentReaderAt(func() (io.ReadCloser, error) {
return ioutil.NopCloser(strings.NewReader("ABCDEFG")), nil
})
io.Copy(os.Stdout, io.NewSectionReader(r, 0, 3))
io.Copy(os.Stdout, io.NewSectionReader(r, 1, 3))
}
如果你大多向前移动,这可能会有效。特别是如果你使用缓冲读卡器。
io.ReaderAt
保证:https://godoc.org/io#ReaderFrom,即它不允许并行调用ReadAt
,并且不会阻止完整读取,所以这甚至可能无法正常工作答案 1 :(得分:0)
我遇到了完全相同的需求,并想出了以下方法,不确定是否对您有帮助:
// NewZipFromReader ...
func NewZipFromReader(file io.ReadCloser, size int64) (*zip.Reader, error) {
in := file.(io.Reader)
if _, ok := in.(io.ReaderAt); ok != true {
buffer, err := ioutil.ReadAll(in)
if err != nil {
return nil, err
}
in = bytes.NewReader(buffer)
size = int64(len(buffer))
}
reader, err := zip.NewReader(in.(io.ReaderAt), size)
if err != nil {
return nil, err
}
return reader, nil
}
因此,如果file
未实现io.ReaderAt
,它将全部内容读入缓冲区。
处理ZIP炸弹可能并不安全,对于大于RAM的文件,OOM肯定会失败。