Search code examples
gozip

How to get the size of zipped folder


Use case is: I want to zip a folder which could contain n number of files with difference sizes. I want to add limit like if the zip size > x (MB|GB) then do not create the zip and throw error. I tried to get the size of zip file after the zip is created after iterating the folder but not able to do so. Request help here. I am using io.writer to create zip file instead buffer. Sample code is

Note: I am able to calculate the size when using "buf := new(bytes.Buffer)" but I want to calculate using io.writer as seen below

filepath - Path to the folder downloadLimit - the limit I want to set for downloaded zip file. Like zip file size should not be more then 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
}

Above function create zip file, but I want it to stop creation as soon the limit is reached and exit OR at least get the size of the zip file created


Solution

  • The standard API does not provide an io.Writer implementaion to do that, afaik. It exists a LimitReader but it is of little use for your case and would led us to write rather complex code.

    Instead, you can write your own io.Writer wrapper implementation to ensure that if the total amount of bytes written onto it exceeds some length, or is about to, return an error.

    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