【问题标题】:'System.OutOfMemoryException' was thrown in ZipArchive for large file from URL'System.OutOfMemoryException' 在 ZipArchive 中针对来自 URL 的大文件被抛出
【发布时间】:2016-10-17 01:47:08
【问题描述】:

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

必须使用 Stream,因为 webClient.OpenRead(Uri Address) 返回一个 Stream。以及使用 ZipArchive(Stream 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);                                                         
                }
             }
         }
    }

【问题讨论】:

  • 我很惊讶您在OpenRead 上收到错误,打开流进行读取不应该占用那么多内存。
  • 什么是 StackTrace?
  • 虽然我在下一行得到了End of Central Directory record could not be found,但在我的最后没有得到这样的错误。
  • @ScottChamberlain 我在使用时遇到错误 (ZipArchive archive = new ZipArchive(streamFile)
  • 这就是我的想法,请参阅我为解决该问题提供的答案。

标签: c# asp.net-mvc stream


【解决方案1】:

我认为here might be your problem,来自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)
}

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

尝试查找处理 Zip 文件且不需要支持搜索的流的 3rd 方库。如果不能,请先将文件复制到磁盘并使用 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);
            }
        }
    }
}

【讨论】:

  • 先将文件复制到临时位置,然后解压缩,Scott 效果很好。我应该首先想到的。感谢您的帮助。
  • 这个问题在 2020 年已经修复了,现在可以使用 getentry 从 url 读取它吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-07
  • 2021-05-20
  • 2013-12-28
  • 2019-07-13
  • 1970-01-01
相关资源
最近更新 更多