46

我有一个内存流,其中包含byte[]格式的 zip 文件。有什么方法可以解压缩这个内存流,而不需要将文件写入磁盘?

一般来说,我ICSharpCode.SharpZipLib.Zip.FastZip用来解压缩文件,但是有没有办法解压缩内存流,可能是通过将文件存储为另一种文件MemoryStreambyte[]根据 zip 中存在的文件/文件夹的格式存储文件?

在这种情况下,我有什么办法可以使用 Memorymapped 文件功能?

4

4 回答 4

123

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

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

在您的项目中,右键单击 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
    }
}

希望这可以帮助。

于 2014-02-13T22:38:13.077 回答
21

我们使用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;
}

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


这是一个变体,它将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;
}
于 2012-10-03T20:23:44.363 回答
16

我刚刚遇到了类似的问题,我发现我认为似乎相当优雅的答案是使用#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();
    }
}

这似乎奏效了。希望这可以帮助。

于 2013-11-29T12:02:23.740 回答
8

是的,从使用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
        }
    }
}

如果您使用的是不可搜索的流,请使用 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();
    }
}

来自ICSharpCode Wiki的示例

于 2012-10-03T21:35:25.890 回答