对于来自 URL 的大文件,在 ZipArchive 中抛出“System.OutOfMemoryException”

Apo*_*llo 2 c# asp.net-mvc stream

我有一个包含 zip 文件的 URL。需要从 URL 中解压缩文件。使用 webclient 打开和读取 URL,然后将其添加到流中。然后在 ZipArchive 对象中使用它,该对象将解压缩文件并将它们存储在 D:\ 驱动器中。当文件大小约为 400Mb 时,我会收到'System.OutOfMemoryException'

由于 webClient.OpenRead(Uri Address) 返回一个 Stream,因此必须使用 Stream。以及使用ZipArchive(Stream流)。

在此输入图像描述

我怎样才能停止收到此消息?

 string zipFileUrl = "https://www.dropbox.com/s/clersbjdcshpdy6/oversize_zip_test_0.zip?dl=0"
 string output_path = @"D:\";

 using (WebClient webClient = new WebClient())
 {

     using (Stream streamFile = webClient.OpenRead(zipFileUrl))
     {
          using (ZipArchive archive = new ZipArchive(streamFile))//ERROR HERE
          {                                 
              var entries = archive.Entries;
              //Loops thru each file in Zip and adds it to directory
              foreach (var entry in entries)
              {
                 if (entry.FullName != "/" && entry.Name != "")
                 {

                    string completeFileName = Path.Combine(output_path, entry.FullName);
                    string directory = Path.GetDirectoryName(completeFileName);

                     //If directory does not exist then we create it.
                     if (!Directory.Exists(directory))
                     {
                         Directory.CreateDirectory(directory);
                     }
                     //Extracts zip from URL to extract path, and overwrites if file exists. 
                     entry.ExtractToFile(completeFileName, true);                                                         
                }
             }
         }
    }
Run Code Online (Sandbox Code Playgroud)

Sco*_*ain 5

从方法上看,我认为这可能是你的问题ZipArchive.Init

private void Init(Stream stream, ZipArchiveMode mode, Boolean leaveOpen)
{
    Stream extraTempStream = null;

    try
    {
        _backingStream = null;

        //check stream against mode
        switch (mode)
        {
            case ZipArchiveMode.Create:
                // (SNIP)
            case ZipArchiveMode.Read:
                if (!stream.CanRead)
                    throw new ArgumentException(SR.ReadModeCapabilities);
                if (!stream.CanSeek)
                {
                    _backingStream = stream;
                    extraTempStream = stream = new MemoryStream();
                    _backingStream.CopyTo(stream);
                    stream.Seek(0, SeekOrigin.Begin);
                }
                break;
            case ZipArchiveMode.Update:
                // (SNIP)
            default:
                // (SNIP)
        }
     // (SNIP)
}
Run Code Online (Sandbox Code Playgroud)

如果streamFile.CanSeek为 false(来自 WebClient 的情况为 false),则会将整个文件复制到内存中,然后处理该文件。这就是耗尽所有内存的原因。

尝试找到一个处理 Zip 文件并且不需要支持查找的流的第 3 方库。如果不能,请先将文件复制到磁盘的临时文件夹中并传入FileStream选项FileOptions.DeleteOnClose,然后在关闭流之前在 zip 中使用该流。

string zipFileUrl = "https://www.dropbox.com/s/clersbjdcshpdy6/oversize_zip_test_0.zip?dl=0";
string output_path = @"D:\";

using (var tempFileStream = new FileStream(Path.GetTempFileName(), FileMode.Create, 
                                           FileAccess.ReadWrite, FileShare.None, 
                                           4096, FileOptions.DeleteOnClose))
{
    using (WebClient webClient = new WebClient())
    {
        using (Stream streamFile = webClient.OpenRead(zipFileUrl))
        {
            streamFile.CopyTo(tempFileStream);
        }
    }
    tempFileStream.Position = 0;
    using (ZipArchive archive = new ZipArchive(tempFileStream))
    {
        var entries = archive.Entries;
        //Loops thru each file in Zip and adds it to directory
        foreach (var entry in entries)
        {
            if (entry.FullName != "/" && entry.Name != "")
            {

                string completeFileName = Path.Combine(output_path, entry.FullName);
                string directory = Path.GetDirectoryName(completeFileName);

                //If directory does not exist then we create it.
                if (!Directory.Exists(directory))
                {
                    Directory.CreateDirectory(directory);
                }
                //Extracts zip from URL to extract path, and overwrites if file exists. 
                entry.ExtractToFile(completeFileName, true);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)