Creating valid XLSX files using SharpZipLib

I am writing a library to work with Excel files. It can generate a valid .xlsx file, except for the zipping up part.

The following code will generate a .ZIP file (as a byte array) that appears to be completely valid. I can unzip it with any .zip tool without a problem.

If, for example, I take a .xlsx file, rename it .zip, and extract all the files to a directory and then zip them up using the code below, rename that from .zip to .xlsx, Excel and LibreOffice will both refuse to open it.

I can, however, unzip that file myself, into a folder and re-zip them up using the OS compression and rename it .xlsx, and it will open just fine. So the contents of the .zip file are fine. The zipping itself is the problem. I've gone through several iterations and this is where it stands and I just can't figure out what I'm missing to satisfy Excel & LibreOffice are expecting. Is there something in the entries that I should be adding to make it a more standard .zip file?

    string _rootPath;
    public byte[] CreateZipFile(string path)
    {
        _rootPath = path;
        using (var ms = new MemoryStream())
        using (var zipStream = new ZipOutputStream(ms))
        {
            RecursiveAddToZipFile(zipStream, path);
            zipStream.Finish();
            zipStream.Close();
            return ms.ToArray();
        }
    }

    private void RecursiveAddToZipFile(ZipOutputStream zipStream, string path)
    {
        foreach(var dir in Directory.GetDirectories(path))
        {
            var entry = new ZipEntry(dir.Replace(_rootPath, "") + @"/");
            RecursiveAddToZipFile(zipStream, dir);
            zipStream.PutNextEntry(entry);
            zipStream.CloseEntry();
        }

        var files = Directory.GetFiles(path);
        foreach(var file in files)
        {
            var entry = new ZipEntry(file.Replace(_rootPath, ""));
            entry.DateTime = DateTime.Now;
            zipStream.PutNextEntry(entry);
            using (FileStream fs = File.OpenRead(file)) 
            {
                int totalBytes = 0;
                byte[] buffer = new byte[1024 * 32];
                int bytesRead;
                do
                {
                    bytesRead = fs.Read(buffer, 0, buffer.Length);
                    totalBytes += bytesRead;
                    zipStream.Write(buffer, 0, bytesRead);
                } while (bytesRead > 0);
                entry.Size = totalBytes;
            }
            zipStream.CloseEntry();
        }
    }

1 answer

  • answered 2022-05-06 21:29 Alex Kovalyov

    According to ECMA-376-2 Office Open Formats Part 2 “Open Packaging Conventions”, the only compression algorithm supported is DEFLATE. So setting CompressionMethod for the entry before adding it to stream should help. Your code worked fine with this addition (Excel 2016 opened the file successfully).

            var entry = new ZipEntry(dir.Replace(_rootPath, "") + @"/");
            entry.CompressionMethod = CompressionMethod.Deflated;
            RecursiveAddToZipFile(zipStream, dir);
            zipStream.PutNextEntry(entry);
            zipStream.CloseEntry();
    

How many English words
do you know?
Test your English vocabulary size, and measure
how many words do you know
Online Test
Powered by Examplum