使用archive / zip处理嵌套的zip文件

时间:2016-10-25 16:46:28

标签: go zip

我正在努力处理Go中的嵌套zip文件(其中zip文件包含另一个zip文件)。我正在尝试递归一个zip文件并列出它包含的所有文件。

archive / zip为您提供了两种处理zip文件的方法:

  • zip.NewReader
  • zip.OpenReader

OpenReader在磁盘上打开一个文件。 NewReader接受io.ReaderAt和文件大小。当您使用其中任何一个迭代压缩文件时,您会为zip中的每个文件获得zip.File。要获取文件f的文件内容,请致电f.Open,它会为您提供zip.ReadCloser。要打开嵌套的zip文件,我需要使用NewReader,但zip.Filezip.ReadCloser不满足io.ReaderAt界面。

zip.File有一个私人字段zipr,其中io.ReaderAtzip.ReadCloser的私有字段fos.File,应该NewReader满足ModelMultipleChoiceField

的要求

我的问题:有没有办法打开嵌套的zip文件,而无需先将内容写入磁盘上的文件,或将整个内容读入内存。

看起来所需的一切都在zip.File中可用,但不会导出。我希望我错过了什么。

2 个答案:

答案 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肯定会失败。