简体   繁体   English

在 C# 中从多个内存文件创建 Zip 存档

[英]Create Zip archive from multiple in memory files in C#

Is there a way to create a Zip archive that contains multiple files, when the files are currently in memory?当文件当前在内存中时,有没有办法创建包含多个文件的 Zip 存档? The files I want to save are really just text only and are stored in a string class in my application.我想保存的文件实际上只是文本,并存储在我的应用程序中的字符串类中。 But I would like to save multiple files in a single self-contained archive.但我想将多个文件保存在一个独立的存档中。 They can all be in the root of the archive.它们都可以位于存档的根目录中。

It would be nice to be able to do this using SharpZipLib.能够使用 SharpZipLib 做到这一点会很好。

Use ZipEntry and PutNextEntry() for this. PutNextEntry()使用ZipEntryPutNextEntry() The following shows how to do it for a file, but for an in-memory object just use a MemoryStream下面显示了如何为文件执行此操作,但对于内存中的对象,只需使用 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();

Using SharpZipLib for this seems pretty complicated.为此使用 SharpZipLib 似乎非常复杂。 This is so much easier in DotNetZip .这在DotNetZip 中要容易得多 In v1.9, the code looks like this:在 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"); 
}

The code above assumes stringContent{1,2,3} contains the data to be stored in the files (or entries) in the zip archive.上面的代码假设 stringContent{1,2,3} 包含要存储在 zip 存档文件(或条目)中的数据。 The first entry is "Readme.txt" and it is stored in the top level "Directory" in the zip archive.第一个条目是“Readme.txt”,它存储在 zip 存档中的顶级“目录”中。 The next two entries are stored in the "readings" directory in the zip archive.接下来的两个条目存储在 zip 存档的“读数”目录中。

The strings are encoded in the default encoding.字符串以默认编码进行编码。 There is an overload of AddEntry(), not shown here, that allows you to explicitly specify the encoding to use. AddEntry() 有一个重载,这里没有显示,它允许您明确指定要使用的编码。

If you have the content in a stream or byte array, not a string, there are overloads for AddEntry() that accept those types.如果您的内容是流或字节数组,而不是字符串,则 AddEntry() 有接受这些类型的重载。 There are also overloads that accept a Write delegate, a method of yours that is invoked to write data into the zip.还有一些重载接受 Write 委托,这是您调用的一种方法,用于将数据写入 zip。 This works for easily saving a DataSet into a zip file, for example.例如,这适用于将数据集轻松保存到 zip 文件中。

DotNetZip is free and open source. DotNetZip 是免费和开源的。

This function should create a byte array from a stream of data: I've created a simple interface for handling files for simplicity这个函数应该从数据流创建一个字节数组:为了简单起见,我已经创建了一个简单的界面来处理文件

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.CopyTo(fileStream);
        }
    }
}

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

I come across this problem, using the MSDN example I created this class:我遇到了这个问题,使用我创建这个类的 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();  
    }  
}

You can then use it like this:然后你可以像这样使用它:

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();

You can pass a MemoryStream (or any Stream ) to ZipSticle.Add such as:您可以将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();

Note this answer is outdated;请注意,此答案已过时; since .Net 4.5, the ZipArchive class allows zipping files in-memory.从 .Net 4.5 开始, ZipArchive类允许在内存中压缩文件。 See johnny 5's answer below for how to use it.有关如何使用它,请参阅下面的 johnny 5 的答案


You could also do it a bit differently, using a Serializable object to store all strings你也可以做一些不同的事情,使用 Serializable 对象来存储所有字符串

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

Then, you could serialize it into a stream to save it.然后,您可以将其序列化为流以进行保存。
To save on space you could use GZipStream (From System.IO.Compression) to compress it.为了节省空间,您可以使用 GZipStream(来自 System.IO.Compression)来压缩它。 (note: GZip is stream compression, not an archive of multiple files). (注意:GZip 是流压缩,不是多个文件的存档)。

That is, of course if what you need is actually to save data, and not zip a few files in a specific format for other software.也就是说,当然,如果您实际上需要的是保存数据,而不是为其他软件以特定格式压缩一些文件。 Also, this would allow you to save many more types of data except strings.此外,这将允许您保存除字符串之外的更多类型的数据。

I was utilizing Cheeso's answer by adding MemoryStream s as the source of the different Excel files.我通过添加MemoryStream作为不同 Excel 文件的源来利用Cheeso 的答案 When I downloaded the zip, the files had nothing in them.当我下载 zip 时,文件中没有任何内容。 This could be the way we were getting around trying to create and download a file over AJAX.这可能是我们尝试通过 AJAX 创建和下载文件的方式。

To get the contents of the different Excel files to be included in the Zip, I had to add each of the files as a byte[] .为了获得要包含在 Zip 中的不同 Excel 文件的内容,我必须将每个文件添加为一个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);
}

Use a StringReader to read from your string objects and expose them as Stream s.使用StringReader从字符串对象中读取并将它们公开为 Stream s。

That should make it easy to feed them to your zip-building code.这应该可以轻松地将它们提供给您的邮政编码。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM