Search code examples
windowsgoziparchive

Golang archive/zip silent error zipping last file


Using go 1.21.4 windows/adm64

I'm experiencing an interesting issue when trying to zip files using go's archive/zip library. Files are all written to the archive, but Windows reports that the zip file is invalid. I can open the achive in 7-Zip, and it shows a "Packed Size" but no "Size" for the last file added to the archive, and a CRC of 00000000 for that file.

Is it possible that the archive file is being closed before the last file has been completely written?

func zipFiles(clipFolder string, zipName string, files []string) int {
    archive, err := os.Create(zipName)
    if err != nil {
        _ = elog.Errorf("Error creating %s: %s", zipName, err.Error())
        return 0
    }
    defer archive.Close()
    writer := zip.NewWriter(archive)

    zipped := 0
    for _, file := range files {
            zipped += writeFileToArchive(writer, clipFolder, file)
        }
    }
    return zipped
}

// writeFileToArchive adds a file to the zip archive and returns the number of files added (0 or 1)
func writeFileToArchive(writer *zip.Writer, path string, filename string) int {
    w, err := writer.Create(filename)
    if err != nil {
        elog.Warnf("could not add %s to archive", filename)
        return 0
    }

    fullPath := filepath.Join(path, filename)
    source, err := os.Open(fullPath)
    if err != nil {
        elog.Warnf("could not open %s file for reading", fullPath)
        return 0
    }
    defer source.Close()
    _, err = io.Copy(w, source)
    if err != nil {
        elog.Warnf("could not copy %s into archive", filename)
        return 0
    }
    _ = writer.Flush()

    return 1
}

I added a call to writer.Flush() after adding each file to the archive, but that doesn't help. And I've tried writing various numbers of files - it's always the last file that isn't being handled properly.


Solution

  • You are not closing writer. Close:

    finishes writing the zip file by writing the central directory. It does not close the underlying writer.

    So with your code the central directory is not being written, this is what Windows is attempting to access. ZIP files have both a central directory (at the end of the file) and a per file local header, I would guess that 7zip is reading the local headers.

    To fix this close the writer (checking for errors is recommended!):

    if err = writer.Close(); err != nil {
        _ = elog.Errorf("Error closing %s: %s", zipName, err.Error())
        return 0
    }