11

I am getting OutofMemoryException while trying to add files to a .zip file. I am using 32-bit architecture for building and running the application.

string[] filePaths = Directory.GetFiles(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "\\capture\\capture");
System.IO.Compression.ZipArchive zip = ZipFile.Open(filePaths1[c], ZipArchiveMode.Update);

foreach (String filePath in filePaths)
{
    string nm = Path.GetFileName(filePath);
    zip.CreateEntryFromFile(filePath, "capture/" + nm, CompressionLevel.Optimal);
}

zip.Dispose();
zip = null;

I am unable to understand the reason behind it.

Peter Duniho
  • 68,759
  • 7
  • 102
  • 136
user3269550
  • 452
  • 4
  • 15
  • Compiling as 64-bit might solve your problem. Alternatively, using `CompressionLevel.Fastest` or `CompressionLevel.NoCompression` is worth a try. – cshu May 01 '15 at 07:44
  • I cant compile as 64bit coz i am using some libraries of 32 bit in my project – user3269550 May 01 '15 at 07:49

2 Answers2

13

The exact reason depends on a variety of factors, but most likely you are simply just adding too much to the archive. Try using the ZipArchiveMode.Create option instead, which writes the archive directly to disk without caching it in memory.

If you are really trying to update an existing archive, you can still use ZipArchiveMode.Create. But it will require opening the existing archive, copying all of its contents to a new archive (using Create), and then adding the new content.

Without a good, minimal, complete code example, it would not be possible to say for sure where the exception is coming from, never mind how to fix it.

EDIT:

Here is what I mean by "…opening the existing archive, copying all of its contents to a new archive (using Create), and then adding the new content":

string[] filePaths = Directory.GetFiles(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "\\capture\\capture");

using (ZipArchive zipFrom = ZipFile.Open(filePaths1[c], ZipArchiveMode.Read))
using (ZipArchive zipTo = ZipFile.Open(filePaths1[c] + ".tmp", ZipArchiveMode.Create))
{
    foreach (ZipArchiveEntry entryFrom in zipFrom.Entries)
    {
        ZipArchiveEntry entryTo = zipTo.CreateEntry(entryFrom.FullName);

        using (Stream streamFrom = entryFrom.Open())
        using (Stream streamTo = entryTo.Open())
        {
            streamFrom.CopyTo(streamTo);
        }
    }

    foreach (String filePath in filePaths)
    {
        string nm = Path.GetFileName(filePath);
        zipTo.CreateEntryFromFile(filePath, "capture/" + nm, CompressionLevel.Optimal);
    }
}

File.Delete(filePaths1[c]);
File.Move(filePaths1[c] + ".tmp", filePaths1[c]);

Or something like that. Lacking a good, minimal, complete code example, I just wrote the above in my browser. I didn't try to compile it, never mind test it. And you may want to adjust some specifics (e.g. the handling of the temp file). But hopefully you get the idea.

Community
  • 1
  • 1
Peter Duniho
  • 68,759
  • 7
  • 102
  • 136
  • Please can u give me example of updating zip by using ZipArchiveMode.Create – user3269550 May 01 '15 at 07:51
  • 2
    As mentioned about the Update/Create options, [Microsoft documentation](https://msdn.microsoft.com/en-us/library/system.io.compression.ziparchivemode(v=vs.110).aspx) states: "When you set the mode to Update, the underlying file or stream must support reading, writing, and seeking. The content of the entire archive is held in memory, and no data is written to the underlying file or stream until the archive is disposed." Just adding it here so the official information is easily found. – Sami Kuhmonen Jul 17 '18 at 16:16
  • To add to this you'll need to copy the entry write time if you want to preserve the entry file dates. E.g., on the line after initializing entryTo: `entryTo.LastWriteTime = entryFrom.LastWriteTime;` – Luke Jun 06 '19 at 15:37
-1

The reason is simple. OutOfMemoryException means memory is not enough for the execution.

Compression consumes a lot of memory. There is no guarantee that a change of logic can solve the problem. But you can consider different methods to alleviate it.

1. Since your main program must be 32-bit, you can consider starting another 64-bit process to do the compression (use System.Diagnostics.Process.Start). After the 64-bit process finishes its job and exits, your 32-bit main program can continue. You can simply use a tool already installed on the system, or write a simple program yourself.

2. Another method is to dispose each time you add an entry. ZipArchive.Dispose saves the file. After each iteration, memory allocated for the ZipArchive can be freed.

foreach (String filePath in filePaths)
{
    System.IO.Compression.ZipArchive zip = ZipFile.Open(filePaths1[c], ZipArchiveMode.Update);
    string nm = Path.GetFileName(filePath);
    zip.CreateEntryFromFile(filePath, "capture/" + nm, CompressionLevel.Optimal);
    zip.Dispose();
}

This approach is not straightforward, and it might not be as effective as the first approach.

The_Black_Smurf
  • 5,178
  • 14
  • 52
  • 78
cshu
  • 5,654
  • 28
  • 44