33

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

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

4

8 回答 8

26

使用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();
于 2008-11-09T19:24:54.817 回答
20

为此使用 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 是免费和开源的。

于 2008-12-21T03:44:34.110 回答
9

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

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);
        }
    }
}
于 2017-07-12T20:40:39.157 回答
5

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

于 2008-11-09T19:17:08.233 回答
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();

您可以将 a 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();
于 2011-10-07T15:24:20.863 回答
2

请注意,此答案已过时;从 .Net 4.5 开始,ZipArchive该类允许在内存中压缩文件。有关如何使用它,请参阅下面的 johnny 5 的答案。


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

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

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

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

于 2008-11-09T20:08:50.750 回答
2

我通过添加s 作为不同 Excel 文件的源来利用Cheeso 的答案。MemoryStream当我下载 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);
}
于 2017-09-20T14:31:21.547 回答
-1

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

这应该可以轻松地将它们提供给您的邮政编码。

于 2008-11-09T19:20:36.120 回答