C# 解压缩内存流(包含 zip 文件)并获取文件

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12715945/
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-10 00:12:31  来源:igfitidea点击:

Unzip a memorystream (Contains the zip file) and get the files

c#

提问by Deepak Jena

I have a memory stream that contains a zip file in byte[] format .

我有一个包含 byte[] 格式的 zip 文件的内存流。

Is there any way I can unzip this memory stream, without any need of writing the file to disk ?

有什么办法可以解压缩这个内存流,而无需将文件写入磁盘?

In general I am using ICSharpCode.SharpZipLib.Zip.FastZip to unzip a file , But any way to unzip a memory stream ? and store the files in another memorystream or in byte[] format according to the files/folders present in the zip ?

一般来说,我使用 ICSharpCode.SharpZipLib.Zip.FastZip 解压缩文件,但有什么方法可以解压缩内存流?并根据 zip 中存在的文件/文件夹将文件存储在另一个内存流或字节 [] 格式中?

Any way I can use the Memorymapped files feature in this scenario ?

在这种情况下,我可以通过什么方式使用 Memorymapped 文件功能?

采纳答案by Bobson

We use DotNetZip, and I can unzip the contents of a zip file from a Streaminto memory. Here's the sample code for extracting a specifically named file from a stream (LocalCatalogZip) and returning a stream to read that file, but it'd be easy to expand on it.

我们使用DotNetZip,我可以将 zip 文件的内容从 a 解压缩Stream到内存中。下面是用于从流 ( LocalCatalogZip) 中提取特定命名文件并返回流以读取该文件的示例代码,但很容易对其进行扩展。

private static MemoryStream UnZipCatalog()
{
    MemoryStream data = new MemoryStream();
    using (ZipFile zip = ZipFile.Read(LocalCatalogZip))
    {
        zip["ListingExport.txt"].Extract(data);
    }
    data.Seek(0, SeekOrigin.Begin);
    return data;
}

It's not the library you're using now, but if you can change, you can get that functionality.

这不是您现在使用的库,但如果您可以更改,则可以获得该功能。



Here's a variation which would return a Dictionary<string,MemoryStream>of for the contents of every file of a zip file.

这是一个变体,它将Dictionary<string,MemoryStream>为 zip 文件的每个文件的内容返回 a 。

private static Dictionary<string,MemoryStream> UnZipToMemory()
{
    var result = new Dictionary<string,MemoryStream>();
    using (ZipFile zip = ZipFile.Read(LocalCatalogZip))
    {
        foreach (ZipEntry e in zip)
        {
            MemoryStream data = new MemoryStream();
            e.Extract(data);
            result.Add(e.FileName, data);
        }
    }

    return result;
}

回答by Scott Chamberlain

Yes, Change from using FastZipTo new ZipFile(stream), but this only works if your stream can seek. (Just use your MemoryStream in new ZipFile(fs);instead of reading a file stream like the example.)

是的,不要使用FastZipTo new ZipFile(stream),但这只有在您的流可以搜索时才有效。(只需使用您的 MemoryStreamnew ZipFile(fs);而不是像示例那样读取文件流。)

C#
using ICSharpCode.SharpZipLib.Core;
using ICSharpCode.SharpZipLib.Zip;

public void ExtractZipFile(string archiveFilenameIn, string password, string outFolder) {
    ZipFile zf = null;
    try {
        FileStream fs = File.OpenRead(archiveFilenameIn);
        zf = new ZipFile(fs);
        if (!String.IsNullOrEmpty(password)) {
            zf.Password = password;     // AES encrypted entries are handled automatically
        }
        foreach (ZipEntry zipEntry in zf) {
            if (!zipEntry.IsFile) {
                continue;           // Ignore directories
            }
            String entryFileName = zipEntry.Name;
            // to remove the folder from the entry:- entryFileName = Path.GetFileName(entryFileName);
            // Optionally match entrynames against a selection list here to skip as desired.
            // The unpacked length is available in the zipEntry.Size property.

            byte[] buffer = new byte[4096];     // 4K is optimum
            Stream zipStream = zf.GetInputStream(zipEntry);

            // Manipulate the output filename here as desired.
            String fullZipToPath = Path.Combine(outFolder, entryFileName);
            string directoryName = Path.GetDirectoryName(fullZipToPath);
            if (directoryName.Length > 0)
                Directory.CreateDirectory(directoryName);

            // Unzip file in buffered chunks. This is just as fast as unpacking to a buffer the full size
            // of the file, but does not waste memory.
            // The "using" will close the stream even if an exception occurs.
            using (FileStream streamWriter = File.Create(fullZipToPath)) {
                StreamUtils.Copy(zipStream, streamWriter, buffer);
            }
        }
    } finally {
        if (zf != null) {
            zf.IsStreamOwner = true; // Makes close also shut the underlying stream
            zf.Close(); // Ensure we release resources
        }
    }
}


If you are using a non-seekable stream use ZipInputStream.

如果您使用的是不可搜索的流,请使用 ZipInputStream。

// Calling example:
    WebClient webClient = new WebClient();
    Stream data = webClient.OpenRead("http://www.example.com/test.zip");
    // This stream cannot be opened with the ZipFile class because CanSeek is false.
    UnzipFromStream(data, @"c:\temp");

public void UnzipFromStream(Stream zipStream, string outFolder) {

    ZipInputStream zipInputStream = new ZipInputStream(zipStream);
    ZipEntry zipEntry = zipInputStream.GetNextEntry();
    while (zipEntry != null) {
        String entryFileName = zipEntry.Name;
        // to remove the folder from the entry:- entryFileName = Path.GetFileName(entryFileName);
        // Optionally match entrynames against a selection list here to skip as desired.
        // The unpacked length is available in the zipEntry.Size property.

        byte[] buffer = new byte[4096];     // 4K is optimum

        // Manipulate the output filename here as desired.
        String fullZipToPath = Path.Combine(outFolder, entryFileName);
        string directoryName = Path.GetDirectoryName(fullZipToPath);
        if (directoryName.Length > 0)
            Directory.CreateDirectory(directoryName);

        // Unzip file in buffered chunks. This is just as fast as unpacking to a buffer the full size
        // of the file, but does not waste memory.
        // The "using" will close the stream even if an exception occurs.
        using (FileStream streamWriter = File.Create(fullZipToPath)) {
            StreamUtils.Copy(zipInputStream, streamWriter, buffer);
        }
        zipEntry = zipInputStream.GetNextEntry();
    }
}

Examples taken from the ICSharpCode Wiki

取自ICSharpCode Wiki 的示例

回答by kipper_t

I've just had a similar issue and the answer I found which I think seems to be fairly elegant is to use #ZipLib(available using nuget) and do the following:

我刚刚遇到了一个类似的问题,我发现我认为似乎相当优雅的答案是使用#ZipLib(使用nuget可用)并执行以下操作:

private byte[] GetUncompressedPayload(byte[] data)
{
    using (var outputStream = new MemoryStream())
    using (var inputStream = new MemoryStream(data))
    {
        using (var zipInputStream = new ZipInputStream(inputStream))
        {
            zipInputStream.GetNextEntry();
            zipInputStream.CopyTo(outputStream);
        }
        return outputStream.ToArray();
    }
}

This seems to have worked a treat. Hope this helps.

这似乎是一种享受。希望这可以帮助。

回答by gigaduck

Yes, .Net 4.5 now supports more Zip functionality.

是的,.Net 4.5 现在支持更多 Zip 功能

Here is a code example based on your description.

这是基于您的描述的代码示例。

In your project, right click on the References folder and add a reference to System.IO.Compression

在您的项目中,右键单击 References 文件夹并添加对System.IO.Compression的引用

using System.IO.Compression;

Stream data = new MemoryStream(); // The original data
Stream unzippedEntryStream; // Unzipped data from a file in the archive

ZipArchive archive = new ZipArchive(data);
foreach (ZipArchiveEntry entry in archive.Entries)
{
    if(entry.FullName.EndsWith(".txt", StringComparison.OrdinalIgnoreCase))
    {
         unzippedEntryStream = entry.Open(); // .Open will return a stream
         // Process entry data here
    }
}

Hope this helps.

希望这可以帮助。