【问题标题】:Unzip a MemoryStream (containing the zip file) and get the files解压缩 MemoryStream(包含 zip 文件)并获取文件
【发布时间】:2012-10-03 20:00:52
【问题描述】:

我有一个内存流,其中包含byte[] 格式的 zip 文件。有什么方法可以解压缩这个内存流,而不需要将文件写入磁盘?

一般来说,我使用ICSharpCode.SharpZipLib.Zip.FastZip 来解压缩文件,但有什么方法可以解压缩内存流,可能是根据存在的文件/文件夹将文件存储为另一个MemoryStreambyte[] 格式在压缩包里?

在这种情况下,我可以通过什么方式使用 Memorymapped 文件功能?

【问题讨论】:

    标签: c#


    【解决方案1】:

    是的,.Net 4.5 now supports more Zip functionality

    这是基于您的描述的代码示例。

    在您的项目中,右键单击 References 文件夹并添加对 System.IO.Compression

    的引用
    using System.IO.Compression;
    
    Stream data = new MemoryStream(); // The original data
    Stream unzippedEntryStream; // Unzipped data from a file in the archive
    
    ZipArchive archive = new ZipArchive(data);
    foreach (ZipArchiveEntry entry in archive.Entries)
    {
        if(entry.FullName.EndsWith(".txt", StringComparison.OrdinalIgnoreCase))
        {
             unzippedEntryStream = entry.Open(); // .Open will return a stream
             // Process entry data here
        }
    }
    

    希望这会有所帮助。

    【讨论】:

    • 感谢您的意见,听起来很棒!我将完成: .... unzippedEntryStream.CopyTo(data); } } 返回数据;
    • 感谢您的意见,听起来很棒!我将完成: .... ZipArchive archive = new ZipArchive(data); if (archive!=null && archive.Entries!=null) { var archlist = archive.Entries.ToList(); ZipArchiveEntry entry = archlist.Find(f => f.Name.Contains(hrdwFileName)); if (entry!=null) { unzippedEntryStream = entry.Open(); unzippedEntryStream.CopyTo(data); unzippedEntryStream = null; } } 存档.Dispose();返回数据; }
    • 所有有趣的游戏,直到你发现输入流是否不可搜索,它将整个文件复制到内存中......
    【解决方案2】:

    我们使用DotNetZip,我可以将压缩文件的内容从Stream 解压缩到内存中。下面是从流 (LocalCatalogZip) 中提取特定命名文件并返回流以读取该文件的示例代码,但很容易对其进行扩展。

    private static MemoryStream UnZipCatalog()
    {
        MemoryStream data = new MemoryStream();
        using (ZipFile zip = ZipFile.Read(LocalCatalogZip))
        {
            zip["ListingExport.txt"].Extract(data);
        }
        data.Seek(0, SeekOrigin.Begin);
        return data;
    }
    

    这不是您现在使用的库,但如果您可以更改,您可以获得该功能。


    这是一个变体,它将为 zip 文件的每个文件的内容返回 Dictionary<string,MemoryStream>

    private static Dictionary<string,MemoryStream> UnZipToMemory()
    {
        var result = new Dictionary<string,MemoryStream>();
        using (ZipFile zip = ZipFile.Read(LocalCatalogZip))
        {
            foreach (ZipEntry e in zip)
            {
                MemoryStream data = new MemoryStream();
                e.Extract(data);
                result.Add(e.FileName, data);
            }
        }
    
        return result;
    }
    

    【讨论】:

    • 你能解释一下"zip["ListingExport.txt"].Extract(data);" ?
    • @user1621791 - 它在 zip 中查找名为“ListingExport.txt”的文件,然后将该文件填充到名为 dataMemoryStream 中。正如我所说,这个示例是针对我们事先知道其名称的文件的,但是您可以查看 here 以获取遍历所有文件的示例。我会把它复制到我的答案中。
    • 什么是 LocalCatalogZip ?是压缩文件的内存流吗?
    • @Rajesh - 我相信它可以是内存流或文件路径。甚至可能是FileInfo.Read() 有很多重载。
    【解决方案3】:

    我刚刚遇到了类似的问题,我发现我认为似乎相当优雅的答案是使用 #ZipLib(可使用 nuget)并执行以下操作:

    private byte[] GetUncompressedPayload(byte[] data)
    {
        using (var outputStream = new MemoryStream())
        using (var inputStream = new MemoryStream(data))
        {
            using (var zipInputStream = new ZipInputStream(inputStream))
            {
                zipInputStream.GetNextEntry();
                zipInputStream.CopyTo(outputStream);
            }
            return outputStream.ToArray();
        }
    }
    

    这似乎是一种享受。希望这会有所帮助。

    【讨论】:

      【解决方案4】:

      是的,从使用 FastZip 更改为 new ZipFile(stream),但这仅在您的流可以搜索时才有效。 (只需在new ZipFile(fs); 中使用您的 MemoryStream,而不是像示例中那样读取文件流。)

      C#
      using ICSharpCode.SharpZipLib.Core;
      using ICSharpCode.SharpZipLib.Zip;
      
      public void ExtractZipFile(string archiveFilenameIn, string password, string outFolder) {
          ZipFile zf = null;
          try {
              FileStream fs = File.OpenRead(archiveFilenameIn);
              zf = new ZipFile(fs);
              if (!String.IsNullOrEmpty(password)) {
                  zf.Password = password;     // AES encrypted entries are handled automatically
              }
              foreach (ZipEntry zipEntry in zf) {
                  if (!zipEntry.IsFile) {
                      continue;           // Ignore directories
                  }
                  String entryFileName = zipEntry.Name;
                  // to remove the folder from the entry:- entryFileName = Path.GetFileName(entryFileName);
                  // Optionally match entrynames against a selection list here to skip as desired.
                  // The unpacked length is available in the zipEntry.Size property.
      
                  byte[] buffer = new byte[4096];     // 4K is optimum
                  Stream zipStream = zf.GetInputStream(zipEntry);
      
                  // Manipulate the output filename here as desired.
                  String fullZipToPath = Path.Combine(outFolder, entryFileName);
                  string directoryName = Path.GetDirectoryName(fullZipToPath);
                  if (directoryName.Length > 0)
                      Directory.CreateDirectory(directoryName);
      
                  // Unzip file in buffered chunks. This is just as fast as unpacking to a buffer the full size
                  // of the file, but does not waste memory.
                  // The "using" will close the stream even if an exception occurs.
                  using (FileStream streamWriter = File.Create(fullZipToPath)) {
                      StreamUtils.Copy(zipStream, streamWriter, buffer);
                  }
              }
          } finally {
              if (zf != null) {
                  zf.IsStreamOwner = true; // Makes close also shut the underlying stream
                  zf.Close(); // Ensure we release resources
              }
          }
      }
      

      如果您使用的是不可搜索的流,请使用 ZipInputStream。

      // Calling example:
          WebClient webClient = new WebClient();
          Stream data = webClient.OpenRead("http://www.example.com/test.zip");
          // This stream cannot be opened with the ZipFile class because CanSeek is false.
          UnzipFromStream(data, @"c:\temp");
      
      public void UnzipFromStream(Stream zipStream, string outFolder) {
      
          ZipInputStream zipInputStream = new ZipInputStream(zipStream);
          ZipEntry zipEntry = zipInputStream.GetNextEntry();
          while (zipEntry != null) {
              String entryFileName = zipEntry.Name;
              // to remove the folder from the entry:- entryFileName = Path.GetFileName(entryFileName);
              // Optionally match entrynames against a selection list here to skip as desired.
              // The unpacked length is available in the zipEntry.Size property.
      
              byte[] buffer = new byte[4096];     // 4K is optimum
      
              // Manipulate the output filename here as desired.
              String fullZipToPath = Path.Combine(outFolder, entryFileName);
              string directoryName = Path.GetDirectoryName(fullZipToPath);
              if (directoryName.Length > 0)
                  Directory.CreateDirectory(directoryName);
      
              // Unzip file in buffered chunks. This is just as fast as unpacking to a buffer the full size
              // of the file, but does not waste memory.
              // The "using" will close the stream even if an exception occurs.
              using (FileStream streamWriter = File.Create(fullZipToPath)) {
                  StreamUtils.Copy(zipInputStream, streamWriter, buffer);
              }
              zipEntry = zipInputStream.GetNextEntry();
          }
      }
      

      来自ICSharpCode Wiki的示例

      【讨论】:

      • 非常感谢您的解决方案,我今天会尝试:)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-08-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多