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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/276319/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 21:11:19  来源:igfitidea点击:

Create Zip archive from multiple in memory files in C#

c#compressionzip

提问by Adam Haile

Is there a way to create a Zip archive that contains multiple files, when the files are currently in memory? 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.

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

It would be nice to be able to do this using SharpZipLib.

能够使用 SharpZipLib 做到这一点会很好。

采纳答案by Cory

Use ZipEntryand PutNextEntry()for this. The following shows how to do it for a file, but for an in-memory object just use a MemoryStream

为此使用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();

回答by Jon Skeet

Yes, you can use SharpZipLib to do this - when you need to supply a stream to write to, use a MemoryStream.

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

回答by chakrit

Use a StringReaderto 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.

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

回答by configurator

Note this answer is outdated; since .Net 4.5, the ZipArchiveclass allows zipping files in-memory. See johnny 5's answer belowfor how to use it.

请注意,此答案已过时;从 .Net 4.5 开始,ZipArchive该类允许在内存中压缩文件。有关如何使用它,请参阅下面的 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. (note: GZip is stream compression, not an archive of multiple files).

然后,您可以将其序列化为流以进行保存。
为了节省空间,您可以使用 GZipStream(来自 System.IO.Compression)来压缩它。(注意: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.

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

回答by Cheeso

Using SharpZipLib for this seems pretty complicated. This is so much easier in DotNetZip. In v1.9, the code looks like this:

为此使用 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"); 
}

The code above assumes stringContent{1,2,3} contains the data to be stored in the files (or entries) in the zip archive. The first entry is "Readme.txt" and it is stored in the top level "Directory" in the zip archive. The next two entries are stored in the "readings" directory in the zip archive.

上面的代码假设 stringContent{1,2,3} 包含要存储在 zip 存档文件(或条目)中的数据。第一个条目是“Readme.txt”,它存储在 zip 存档中的顶级“目录”中。接下来的两个条目存储在 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. There are also overloads that accept a Write delegate, a method of yours that is invoked to write data into the zip. This works for easily saving a DataSet into a zip file, for example.

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

DotNetZip is free and open source.

DotNetZip 是免费和开源的。

回答by Ashleigh

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.Addsuch as:

您可以将 a MemoryStream(或 any 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();

回答by johnny 5

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

回答by krillgar

I was utilizing Cheeso's answerby adding MemoryStreams as the source of the different Excel files. When I downloaded the zip, the files had nothing in them. This could be the way we were getting around trying to create and download a file over AJAX.

我通过添加s 作为不同 Excel 文件的源来利用Cheeso 的答案MemoryStream。当我下载 zip 时,文件中没有任何内容。这可能是我们尝试通过 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);
}