如何获取压缩文件夹的大小

How to get the size of zipped folder

用例是: 我想压缩一个文件夹,其中可能包含 n 个不同大小的文件。我想添加限制,如果 zip 大小 > x (MB|GB) 则不创建 zip 并抛出错误。在迭代文件夹后创建 zip 后,我试图获取 zip 文件的大小,但无法这样做。在这里请求帮助。我正在使用 io.writer 创建 zip 文件而不是缓冲区。示例代码是

注意: 我可以使用“buf := new(bytes.Buffer)”来计算大小,但我想使用 io.writer 来计算如下所示

filepath - 文件夹路径 downloadLimit - 我想为下载的 zip 文件设置的限制。像 zip 文件大小不应超过 100 MB

func GetZipFileSize(filepath string, downloadLimit int, w io.Writer) error {
    
    zipWriter := zip.NewWriter(w)

    // List files
    var files []FileAndPath
    var err error
    
    // We have function to fetch files in variable "files" which is used below

    // ZIP files
    for _, file := range files {
        
        // Determine filename in ZIP
        zipFilename := file.Path
        
        filename := fp.Join(SOME_PATH, file.Path)

        // Create ZIP entry
        fileToZip, err := os.Open(filename)
        if err != nil {
            return SOME_ERROR
        }
        info, err := fileToZip.Stat()
        if err != nil {
            return err
        }
        header, err := zip.FileInfoHeader(info)
        if err != nil {
            return err
        }
        header.Name = zipFilename
        header.Method = zip.Deflate
        writer, err := zipWriter.CreateHeader(header)
        if err != nil {
            return err
        }

        // Set content for ZIP entry
        if _, err = io.Copy(writer, fileToZip); err != nil {
            return err
        }
        if err = fileToZip.Close(); err != nil {
            // Nothing
        }
    }

    // Wrap-up
    if err := zipWriter.Close(); err != nil {
        return err
    }

    return nil
}

以上函数创建 zip 文件,但我希望它在达到限制后立即停止创建并退出,或者至少获取创建的 zip 文件的大小

这是解决此问题的建议。代码中的解释。 我认为没有(简单的)方法可以 calculate/estimate 在不执行实际压缩的情况下确定未来 zip 文件的大小。

代码的第 1 行假定您已经有了要压缩的文件的某种字节表示形式。

func main() {       

    // Create a buffer to write our archive to (in memory)
    buf := new(bytes.Buffer)

    // Create a new zip archive writer, that writes to our in memory buffer
    archiveWriter := zip.NewWriter(buf)

    // ensure to close your zip writer later
    defer archiveWriter.Close()

    // you probably want to do this in a loop and repeat for each file you want add to the zip archive
    // this is the content of a file you want to zip / instead read it from disk with ioutil.ReadFile() or some other approach
    dummyBytes := []byte("ThisCouldBeTheContentOfYourSourceFile")
    fileWriter, _ := archiveWriter.Create("fileNameOfSourceFileInZip")
    _, _ = fileWriter.Write(dummyBytes)
    
    
    // validate the in memory representation of the zip file does not exceed your limit
    if buf.Len() > 1000 {
        // todo raise error
        return
    }
    
    // write the zip file to the disk, you could ofc also use io.writer
    _ = ioutil.WriteFile("TargetZipFile.zip", buf.Bytes(), 0644)
}

请注意,我没有实施任何错误处理 - 您可能需要添加它。

标准 API 不提供 io.Writer implementaion to do that, afaik. It exists a LimitReader 但它对您的情况没有多大用处,并且会导致我们编写相当复杂的代码。

相反,您可以编写自己的 io.Writer 包装器实现,以确保如果写入其中的总字节数超过或即将超过某个长度,return 会出错。

package main

import (
    "errors"
    "fmt"
    "io"
    "os"
    "strings"
)

func main() {
    bigBuf := strings.NewReader(strings.Repeat("borng string", 10000))

    dst := &LimitWriter{Writer: os.Stdout, N: 20}

    n, err := io.Copy(dst, bigBuf)
    fmt.Println("n=", n)
    fmt.Println("err=", err)
}

var ErrWriteOverflow = errors.New("write overflow, data is too large")

type LimitWriter struct {
    io.Writer
    N       int64
    written int64
}

func (l *LimitWriter) Write(p []byte) (n int, err error) {
    if l.written+int64(len(p)) >= l.N {
        return 0, ErrWriteOverflow
    }
    n, err = l.Writer.Write(p)
    l.written += int64(n)
    return
}

https://play.golang.org/p/ktgF-pOzFjm