Skip to content

Suggest using ZipArchiveMode.Create when compressing files without modifying their ZipArchivEntry objects #35815

@carlossanlop

Description

@carlossanlop

Consider the following code:

using System.IO;
using System.IO.Compression;

namespace ConsoleApp
{
    class Program
    {
        static void Main()
        {
            DirectoryInfo directory = new DirectoryInfo(@"C:\path\to\directory\with\huge\files\to\compress");

            using (FileStream stream = new FileStream(@"C:\path\to\new\Compressed.zip", FileMode.CreateNew))
            {
                using (ZipArchive archive = new ZipArchive(stream, ZipArchiveMode.Update /* this is the problem */, leaveOpen: false))
                {
                    foreach (FileInfo file in directory.EnumerateFiles("*", SearchOption.AllDirectories))
                    {
                        archive.CreateEntryFromFile(
                            sourceFileName: file.FullName,
                            entryName: file.Name,
                            CompressionLevel.Optimal); // throws
                    }
                }
            }
        }
    }
}

The exception would be:

Backup failed with exception System.IO.IOException: Stream was too long.
   at System.IO.MemoryStream.Write(Byte[] buffer, Int32 offset, Int32 count)
   at System.IO.Stream.InternalCopyTo(Stream destination, Int32 bufferSize)
   at System.IO.Compression.ZipFileExtensions.DoCreateEntryFromFile(ZipArchive destination, String sourceFileName, String entryName, Nullable`1 compressionLevel)

CreateEntryFromFile has this exception documented here.
The exception is thrown because we restrict the size of a file to Int32.MaxValue. The restriction is reached because the code is opening the ZipArchive with ZipArchiveMode.Update. That mode requires a stream that allows seeking, so we internally create a MemoryStream so that we can update the archive, but the size limit for the file is Int32.MaxValue.

The ZipArchiveMode remarks state that ZipArchiveMode.Update allows both reading and writing, but in this code, writing is not needed.

The Roslyn analyzer would only create a diagnostic if we can detect that the code:

  • Is creating a new ZipArchive with FileMode.Create or FileMode.CreateNew, and
    • Is either calling CreateEntryFromFile, which takes the file name and creates the ZipArchiveEntry internally (and it does not get updated), or
    • Is creating ZipArchiveEntry instances but does not manipulate them before adding them to the ZipArchive.

The Roslyn fixer would suggest changing ZipArchiveMode.Update to ZipArchiveMode.Create.

Is this a good candidate for a Roslyn analyzer/fixer?

Metadata

Metadata

Assignees

No one assigned

    Labels

    api-suggestionEarly API idea and discussion, it is NOT ready for implementationarea-System.IO.Compressioncode-analyzerMarks an issue that suggests a Roslyn analyzercode-fixerMarks an issue that suggests a Roslyn code fixer

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions