📜  C# Zip 大文件导致 OOM 异常 - C# (1)

📅  最后修改于: 2023-12-03 15:29:46.520000             🧑  作者: Mango

C# Zip 大文件导致 OOM 异常

在使用C#进行大文件压缩时,可能会遇到OOM(Out of Memory)异常。这种异常通常发生在内存无法容纳需要处理的数据量时。本文将介绍如何避免这种异常。

原因分析

我们知道,Zip压缩是将多个文件打包成一个文件,通过压缩减少文件大小。当需要压缩大文件时,如果一次性读取整个文件进行压缩,就会导致内存溢出。

解决方案

解决OOM异常的方法很简单,只需要将大文件分割成小文件,然后分别进行压缩,最后将压缩后的小文件合并成最终的Zip文件。

下面是示例代码:

using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;

namespace ZipDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            string sourceFile = @"D:\test\bigfile.txt";
            string zipFile = @"D:\test\bigfile.zip";
            int bufferSize = 1024 * 1024 * 100; // 每个小文件大小为100MB

            SplitAndZip(sourceFile, zipFile, bufferSize);

            Console.WriteLine("Done!");
            Console.ReadKey();
        }

        static void SplitAndZip(string sourceFile, string zipFile, int bufferSize)
        {
            using (FileStream fs = new FileStream(sourceFile, FileMode.Open, FileAccess.Read))
            {
                int index = 0;
                byte[] buffer = new byte[bufferSize];

                while (true)
                {
                    int readCount = fs.Read(buffer, 0, buffer.Length);

                    if (readCount <= 0)
                    {
                        break;
                    }

                    string tempFilePath = Path.Combine(Path.GetDirectoryName(sourceFile), $"{Path.GetFileNameWithoutExtension(sourceFile)}_{index}.zip");

                    using (FileStream tempFs = new FileStream(tempFilePath, FileMode.Create, FileAccess.Write))
                    {
                        using (ZipArchive archive = new ZipArchive(tempFs, ZipArchiveMode.Create))
                        {
                            string entryName = $"{Path.GetFileNameWithoutExtension(sourceFile)}_{index}.txt";

                            ZipArchiveEntry entry = archive.CreateEntry(entryName);

                            using (Stream entryStream = entry.Open())
                            {
                                entryStream.Write(buffer, 0, readCount);
                            }
                        }
                    }

                    index++;
                }
            }

            List<string> tempFileList = new List<string>();

            for (int i = 0; i < int.MaxValue; i++)
            {
                string tempFilePath = Path.Combine(Path.GetDirectoryName(sourceFile), $"{Path.GetFileNameWithoutExtension(sourceFile)}_{i}.zip");

                if (File.Exists(tempFilePath))
                {
                    tempFileList.Add(tempFilePath);
                }
                else
                {
                    break;
                }
            }

            using (FileStream finalFs = new FileStream(zipFile, FileMode.Create, FileAccess.Write))
            {
                using (ZipArchive archive = new ZipArchive(finalFs, ZipArchiveMode.Create))
                {
                    foreach (string tempFilePath in tempFileList)
                    {
                        string entryName = Path.GetFileName(tempFilePath);

                        archive.CreateEntryFromFile(tempFilePath, entryName);
                    }
                }
            }

            foreach (string tempFilePath in tempFileList)
            {
                File.Delete(tempFilePath);
            }
        }
    }
}

这个示例代码将源文件分割成100MB大小的小文件,然后每个小文件压缩成一个临时Zip文件,并将所有临时Zip文件合并成一个最终的Zip文件。最后再将所有临时Zip文件删除。

总结

通过将大文件分割成小文件,避免一次性读取整个文件进行压缩,就能有效避免OOM异常的发生。