【问题标题】:Create Zip archive from multiple in memory files in C#从 C# 中的多个内存文件创建 Zip 存档
【发布时间】:2008-11-09 19:04:00
【问题描述】:

当文件当前在内存中时,有没有办法创建包含多个文件的 Zip 存档?我要保存的文件实际上只是文本,并且存储在我的应用程序的字符串类中。但我想将多个文件保存在一个独立的存档中。它们都可以在存档的根目录中。

如果能够使用 SharpZipLib 做到这一点,那就太好了。

【问题讨论】:

    标签: c# compression zip


    【解决方案1】:

    为此使用ZipEntryPutNextEntry()。下面展示了如何对文件执行此操作,但对于内存中的对象只需使用 MemoryStream

    FileStream fZip = File.Create(compressedOutputFile);
    ZipOutputStream zipOStream = new ZipOutputStream(fZip);
    foreach (FileInfo fi in allfiles)
    {
        ZipEntry entry = new ZipEntry((fi.Name));
        zipOStream.PutNextEntry(entry);
        FileStream fs = File.OpenRead(fi.FullName);
        try
        {
            byte[] transferBuffer[1024];
            do
            {
                bytesRead = fs.Read(transferBuffer, 0, transferBuffer.Length);
                zipOStream.Write(transferBuffer, 0, bytesRead);
            }
            while (bytesRead > 0);
        }
        finally
        {
            fs.Close();
        }
    }
    zipOStream.Finish();
    zipOStream.Close();
    

    【讨论】:

    • 你能在zipOStream 上使用'using()' 而不是Close()吗?
    • 是的,ZipOutputStream 是一次性的
    • 如果您使用的是 .NET 4.5,则有一个内置的 Zip 类,它比这种可恶的 (SharpZipLib) 更容易使用。
    • 我认为这需要zipOStream.CloseEntry()在每次枚举之后(即在foreach的末尾)——github.com/icsharpcode/SharpZipLib/wiki/…
    • @TheMuffinMan 关心链接/详细说明替代方案? this answer 是否包裹它?
    【解决方案2】:

    为此使用 SharpZipLib 似乎相当复杂。这在DotNetZip 中要容易得多。在 v1.9 中,代码如下所示:

    using (ZipFile zip = new ZipFile())
    {
        zip.AddEntry("Readme.txt", stringContent1);
        zip.AddEntry("readings/Data.csv", stringContent2);
        zip.AddEntry("readings/Index.xml", stringContent3);
        zip.Save("Archive1.zip"); 
    }
    

    上面的代码假定 stringContent{1,2,3} 包含要存储在 zip 存档文件(或条目)中的数据。第一个条目是“Readme.txt”,它存储在 zip 存档的顶级“目录”中。接下来的两个条目存储在 zip 存档的“读数”目录中。

    字符串以默认编码进行编码。 AddEntry() 有一个重载,此处未显示,它允许您显式指定要使用的编码。

    如果您的内容是流或字节数组,而不是字符串,则 AddEntry() 有接受这些类型的重载。还有一些接受 Write 委托的重载,这是您的一种方法,被调用以将数据写入 zip。例如,这适用于轻松地将 DataSet 保存到 zip 文件中。

    DotNetZip 是免费且开源的。

    【讨论】:

      【解决方案3】:

      这个函数应该从数据流中创建一个字节数组:为了简单起见,我创建了一个简单的接口来处理文件

      public interface IHasDocumentProperties
      {
          byte[] Content { get; set; }
          string Name { get; set; }
      }
      
      public void CreateZipFileContent(string filePath, IEnumerable<IHasDocumentProperties> fileInfos)
      {    
          using (var memoryStream = new MemoryStream())
          {
              using (var zipArchive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true))
              {
                  foreach(var fileInfo in fileInfos)
                  {
                      var entry = zipArchive.CreateEntry(fileInfo.Name);
      
                      using (var entryStream = entry.Open())
                      {
                          entryStream.Write(fileInfo.Content, 0, fileInfo.Content.Length);
                      }                        
                  }
              }
             
              using (var fileStream = new FileStream(filePath, FileMode.OpenOrCreate, System.IO.FileAccess.Write))
              {
                  memoryStream.Position = 0;
                  memoryStream.CopyTo(fileStream);
              }
          }
      }
      

      【讨论】:

      • 这很好,除非您在处理内存流后使用它。
      • 很好,我现在修好了
      • 谢谢,但是使用您的代码生成的zip有0字节,问题在于将memoryStream复制到fileStream,需要在使用memoryStream.Position = 0的CopyTo之前将memoryStream的位置设置为0;然后它按预期工作。参考:stackoverflow.com/a/18766138/3019403
      • @miguelug 谢谢我更新了我的答案
      【解决方案4】:

      是的,您可以使用 SharpZipLib 来执行此操作 - 当您需要提供要写入的流时,请使用 MemoryStream

      【讨论】:

        【解决方案5】:

        我遇到了这个问题,使用我创建的这个类的 MSDN 示例:

        using System;  
        using System.Collections.Generic;  
        using System.Linq;  
        using System.Text;  
        using System.IO.Packaging;  
        using System.IO;  
        
        public class ZipSticle  
        {  
            Package package;  
        
            public ZipSticle(Stream s)  
            {  
                package = ZipPackage.Open(s, FileMode.Create);  
            }  
        
            public void Add(Stream stream, string Name)  
            {  
                Uri partUriDocument = PackUriHelper.CreatePartUri(new Uri(Name, UriKind.Relative));  
                PackagePart packagePartDocument = package.CreatePart(partUriDocument, "");  
        
                CopyStream(stream, packagePartDocument.GetStream());  
                stream.Close();  
            }  
        
            private static void CopyStream(Stream source, Stream target)  
            {  
                const int bufSize = 0x1000;  
                byte[] buf = new byte[bufSize];  
                int bytesRead = 0;  
                while ((bytesRead = source.Read(buf, 0, bufSize)) > 0)  
                    target.Write(buf, 0, bytesRead);  
            }  
        
            public void Close()  
            {  
                package.Close();  
            }  
        }
        

        然后你可以像这样使用它:

        FileStream str = File.Open("MyAwesomeZip.zip", FileMode.Create);  
        ZipSticle zip = new ZipSticle(str);  
        
        zip.Add(File.OpenRead("C:/Users/C0BRA/SimpleFile.txt"), "Some directory/SimpleFile.txt");  
        zip.Add(File.OpenRead("C:/Users/C0BRA/Hurp.derp"), "hurp.Derp");  
        
        zip.Close();
        str.Close();
        

        您可以将MemoryStream(或任何Stream)传递给ZipSticle.Add,例如:

        FileStream str = File.Open("MyAwesomeZip.zip", FileMode.Create);  
        ZipSticle zip = new ZipSticle(str);  
        
        byte[] fileinmem = new byte[1000];
        // Do stuff to FileInMemory
        MemoryStream memstr = new MemoryStream(fileinmem);
        zip.Add(memstr, "Some directory/SimpleFile.txt");
        
        memstr.Close();
        zip.Close();
        str.Close();
        

        【讨论】:

          【解决方案6】:

          注意这个答案已经过时了;从 .Net 4.5 开始,ZipArchive 类允许在内存中压缩文件。使用方法见johnny 5's answer below


          你也可以做一些不同的事情,使用 Serializable 对象来存储所有字符串

          [Serializable]
          public class MyStrings {
              public string Foo { get; set; }
              public string Bar { get; set; }
          }
          

          然后,您可以将其序列化为流进行保存。
          为了节省空间,您可以使用 GZipStream (From System.IO.Compression) 来压缩它。 (注意:GZip 是流压缩,不是多个文件的存档)。

          也就是说,当然,如果您实际需要的是保存数据,而不是压缩一些特定格式的文件以供其他软件使用。 此外,这将允许您保存除字符串之外的更多类型的数据。

          【讨论】:

          • 我有点想让保存文件的格式允许用户打开它并提取特定的部分,但这可能是最好/最简单的事情......
          • @Configurator 很高兴在这里见到你
          【解决方案7】:

          我通过添加 MemoryStreams 作为不同 Excel 文件的源来利用 Cheeso's answer。当我下载 zip 时,文件中没有任何内容。这可能是我们试图通过 AJAX 创建和下载文件的方式。

          为了将不同 Excel 文件的内容包含在 Zip 中,我必须将每个文件添加为 byte[]

          using (var memoryStream = new MemoryStream())
          using (var zip = new ZipFile())
          {
              zip.AddEntry("Excel File 1.xlsx", excelFileStream1.ToArray());
              zip.AddEntry("Excel File 2.xlsx", excelFileStream2.ToArray());
          
              // Keep the file off of disk, and in memory.
              zip.Save(memoryStream);
          }
          

          【讨论】:

            【解决方案8】:

            使用StringReader 从您的字符串对象中读取并将它们公开为 Stream s。

            这应该可以很容易地将它们提供给您的邮政编码。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2015-01-21
              • 2012-04-18
              • 2017-02-07
              • 2020-04-30
              • 2016-09-23
              • 2022-06-24
              • 1970-01-01
              相关资源
              最近更新 更多