【问题标题】:How can I unzip a file to a .NET memory stream?如何将文件解压缩到 .NET 内存流?
【发布时间】:2014-05-01 13:06:47
【问题描述】:

我有文件(来自第 3 方)正在通过 FTP 传输到我们服务器上的某个目录。我下载它们并处理它们甚至'x'分钟。效果很好。

现在,一些文件是.zip 文件。这意味着我无法处理它们。我需要先解压缩它们。

FTP 没有压缩/解压缩的概念 - 所以我需要抓取 zip 文件,解压缩,然后处理它。

MSDN zip api,好像没办法解压到内存流?

所以这是唯一的方法......

  1. 解压到一个文件(什么目录?需要一些非常临时的位置...)
  2. 读取文件内容
  3. 删除文件。

注意:文件的内容很小 - 比如 4k 1000k。

【问题讨论】:

    标签: c# .net ftp


    【解决方案1】:

    Zip 压缩支持内置:

    using System.IO;
    using System.IO.Compression;
    // ^^^ requires a reference to System.IO.Compression.dll
    static class Program
    {
        const string path = ...
        static void Main()
        {
            using(var file = File.OpenRead(path))
            using(var zip = new ZipArchive(file, ZipArchiveMode.Read))
            {
                foreach(var entry in zip.Entries)
                {
                    using(var stream = entry.Open())
                    {
                        // do whatever we want with stream
                        // ...
                    }
                }
            }
        }
    }
    

    通常您应该避免将其复制到另一个流中 - 只需“按原样”使用它,但是,如果您在 MemoryStream绝对需要它,您可以这样做:

    using(var ms = new MemoryStream())
    {
        stream.CopyTo(ms);
        ms.Position = 0; // rewind
        // do something with ms
    }
    

    【讨论】:

    • @Uriil 好吧,首先我不确定为什么该类甚至存在:ZipFile 上的所有方法实际上都是关于 ZipArchive 类 - 对我来说,它们都应该是静态成员在ZipArchive!但更具体地说,因为 OP 正在谈论从现有来源获取数据 - 在本例中为 FTP。在那种情况下,您不能保证您有一个 file,但您通常可以假设您有一个 stream。因此,从流中展示如何做到这一点更具可重用性,并且适用于任何上下文,而不仅仅是文件。但可以肯定:你可以在这里使用ZipFile.OpenRead
    • @Uriil,ZipFile 需要额外的程序集引用 (System.IO.Compression.FileSystem.dll),只是为了避免简单的 File.OpenRead - 似乎不值得
    • 仅在 .net 4.5 及更高版本中。不支持XP
    • @linquize 作为专业人士,我们会做好自己不支持 XP:这样做会使我们的客户/客户面临风险(通过提供隐含的批准)。该操作系统已正式死亡。最后一个 EOL 日期大约是 2 周后。 “2014 年 4 月 8 日之后,Windows XP 的支持和安全更新将不再可用。”
    • @JasonBaley 要求我们先发制人地将所有内容压缩到内存中;利弊
    【解决方案2】:

    您可以使用ZipArchiveEntry.Open 获取流。

    此代码假定 zip 存档有一个文本文件。

    using (FileStream fs = new FileStream(path, FileMode.Open))
    using (ZipArchive zip = new ZipArchive(fs) )
    {
        var entry = zip.Entries.First();
    
        using (StreamReader sr = new StreamReader(entry.Open()))
        {
            Console.WriteLine(sr.ReadToEnd());
        }
    }
    

    【讨论】:

    • 明显的评论:如果数据不是文本,或者数据采用不寻常的编码但缺少 BOM,则会以令人讨厌的方式中断
    • @Gusdor 为什么要编辑? IMO 原版更可取,而且更好,但无论哪种方式,这似乎都不值得编辑
    • @MarcGravell 我觉得对于那些可能不欣赏省略括号行为的读者来说,它使代码更加明确。
    • @MarcGravell 是的,我添加了StreamReader 只是为了展示最简单的用例。当然,如果您阅读的不是文本,那么StreamReader.ReadToEnd 不是您要查找的内容。 (我恢复了 Gusdor 的编辑)。
    • @Gusdor 更易于阅读,更易于理解(IMO),并防止代码向右爆炸。代码可读性是一项功能。
    【解决方案3】:
    using (ZipArchive archive = new ZipArchive(webResponse.GetResponseStream()))
    {
         foreach (ZipArchiveEntry entry in archive.Entries)
         {
            Stream s = entry.Open();
            var sr = new StreamReader(s);
            var myStr = sr.ReadToEnd();
         }
    } 
    

    【讨论】:

    • 你需要一个 Stream sStreamReader sr 的 using 语句来自动关闭它们。
    【解决方案4】:

    看起来这是你需要的:

    using (var za = ZipFile.OpenRead(path))
    {
        foreach (var entry in za.Entries)
        {
            using (var r = new StreamReader(entry.Open()))
            {
                //your code here
            }
        }
    }
    

    【讨论】:

      【解决方案5】:

      好的,结合以上所有内容,假设您想以一种非常简单的方式获取一个名为 “file.zip”并将其解压缩到“C:\temp”文件夹。 (注意:这个例子只测试了压缩文本文件)你可能需要对二进制文件做一些修改。

              using System.IO;
              using System.IO.Compression;
      
              static void Main(string[] args)
              {
                  //Call it like this:
                  Unzip("file.zip",@"C:\temp");
              }
      
              static void Unzip(string sourceZip, string targetPath)
              {
                  using (var z = ZipFile.OpenRead(sourceZip))
                  {
                      foreach (var entry in z.Entries)
                      {                    
                          using (var r = new StreamReader(entry.Open()))
                          {
                              string uncompressedFile = Path.Combine(targetPath, entry.Name);
                              File.WriteAllText(uncompressedFile,r.ReadToEnd());
                          }
                      }
                  }
      
              }
      

      【讨论】:

        【解决方案6】:

        您可以在各种其他库中使用SharpZipLib 来实现此目的。

        您可以使用以下代码示例解压到MemoryStreamas shown on their wiki

        using ICSharpCode.SharpZipLib.Zip;
        
        // Compresses the supplied memory stream, naming it as zipEntryName, into a zip,
        // which is returned as a memory stream or a byte array.
        //
        public MemoryStream CreateToMemoryStream(MemoryStream memStreamIn, string zipEntryName) {
        
            MemoryStream outputMemStream = new MemoryStream();
            ZipOutputStream zipStream = new ZipOutputStream(outputMemStream);
        
            zipStream.SetLevel(3); //0-9, 9 being the highest level of compression
        
            ZipEntry newEntry = new ZipEntry(zipEntryName);
            newEntry.DateTime = DateTime.Now;
        
            zipStream.PutNextEntry(newEntry);
        
            StreamUtils.Copy(memStreamIn, zipStream, new byte[4096]);
            zipStream.CloseEntry();
        
            zipStream.IsStreamOwner = false;    // False stops the Close also Closing the underlying stream.
            zipStream.Close();          // Must finish the ZipOutputStream before using outputMemStream.
        
            outputMemStream.Position = 0;
            return outputMemStream;
        
            // Alternative outputs:
            // ToArray is the cleaner and easiest to use correctly with the penalty of duplicating allocated memory.
            byte[] byteArrayOut = outputMemStream.ToArray();
        
            // GetBuffer returns a raw buffer raw and so you need to account for the true length yourself.
            byte[] byteArrayOut = outputMemStream.GetBuffer();
            long len = outputMemStream.Length;
        }
        

        【讨论】:

        • 注意:您不需要外部库 - BCL 中实际上多次存在 zip 支持
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-01-03
        • 1970-01-01
        • 1970-01-01
        • 2019-01-30
        • 2022-12-21
        • 2013-12-29
        相关资源
        最近更新 更多