使用System.IO.Compression创建的zip文件无效

25

我正在尝试创建一个包含一个或多个文件的zip文件。
我正在使用.NET框架4.5,更具体地是System.IO.Compression命名空间。
目标是允许用户通过ASP.NET MVC应用程序下载zip文件。
zip文件已经生成并发送到客户端,但是当我尝试双击它以打开时,会出现以下错误:
Windows无法打开文件夹。 压缩(zipped)文件……无效。
这是我的代码:

[HttpGet]
public FileResult Download()
{
    var fileOne = CreateFile(VegieType.POTATO);
    var fileTwo = CreateFile(VegieType.ONION);
    var fileThree = CreateFile(VegieType.CARROT);

    IEnumerable<FileContentResult> files = new List<FileContentResult>() { fileOne, fileTwo, fileThree };
    var zip = CreateZip(files);

    return zip;
}

private FileContentResult CreateFile(VegieType vType)
{
    string fileName = string.Empty;
    string fileContent = string.Empty;

    switch (vType)
    {
        case VegieType.BATATA:
            fileName = "batata.csv";
            fileContent = "THIS,IS,A,POTATO";
            break;
        case VegieType.CEBOLA:
            fileName = "cebola.csv";
            fileContent = "THIS,IS,AN,ONION";
            break;
        case VegieType.CENOURA:
            fileName = "cenoura.csv";
            fileContent = "THIS,IS,A,CARROT";
            break;
        default:
            break;
    }

    var fileBytes = Encoding.GetEncoding(1252).GetBytes(fileContent);
    return File(fileBytes, MediaTypeNames.Application.Octet, fileName);
}

private FileResult CreateZip(IEnumerable<FileContentResult> files)
{
    byte[] retVal = null;

    if (files.Any())
    {
        using (MemoryStream zipStream = new MemoryStream())
        {
            using (ZipArchive archive = new ZipArchive(zipStream, ZipArchiveMode.Create, false))
            {
                foreach (var f in files)
                {
                    var entry = archive.CreateEntry(f.FileDownloadName, CompressionLevel.Fastest);
                    using (var entryStream = entry.Open())
                    {
                        entryStream.Write(f.FileContents, 0, f.FileContents.Length);
                        entryStream.Close();
                    }
                }

                zipStream.Position = 0;
                retVal = zipStream.ToArray();
            }
        }
    }

    return File(retVal, MediaTypeNames.Application.Zip, "horta.zip");
}

请问为什么我双击 ZIP 文件时,Windows 会提示它是无效的?
最后需要考虑一下的是,我可以使用 7-Zip 打开它。

6个回答

38

在ZipArchive对象被处理后,您需要通过ToArray获取MemoryStream缓冲区。否则,您将得到损坏的存档。

请注意,我已更改了ZipArchive构造函数的参数以在添加条目时保持其打开状态。

当ZipArchive被处理时会进行一些校验和计算,因此如果您在之前读取MemoryStream,它仍然是不完整的。

    private FileResult CreateZip(IEnumerable<FileContentResult> files)
    {
        byte[] retVal = null;

        if (files.Any())
        {
            using (MemoryStream zipStream = new MemoryStream())
            {
                using (ZipArchive archive = new ZipArchive(zipStream, ZipArchiveMode.Create, true))
                {
                    foreach (var f in files)
                    {
                        var entry = archive.CreateEntry(f.FileDownloadName, CompressionLevel.Fastest);
                        using (BinaryWriter writer = new BinaryWriter(entry.Open()))
                        {                                   
                            writer.Write(f.FileContents, 0, f.FileContents.Length);
                            writer.Close();
                        }
                    }

                    zipStream.Position = 0;
                }
                retVal = zipStream.ToArray();
            }
        }

        return File(retVal, MediaTypeNames.Application.Zip, "horta.zip");
    }

OP说他能够使用7-Zip打开它。因此,Dispose不应该是问题。 - Gaurav P
在处理ZipArchive之前,是否需要将内存流的位置设置为0?我认为ZipArchive的Dispose会对zip文件进行一些最终处理。 - David
非常感谢您,Michal,您是正确的。现在它能工作了。 - César Lourenço
4
在我的测试中发现,我不需要创建一个MemoryStream缓冲区(这很好,因为我正在创建一个14GB的zip文件备份),我只需要显式地调用ZipArchive的Dispose方法即可。这是在VS2017 Community .NET(4.6)控制台项目中实现的。 - John Ernest
@GauravKP确实,OP说他可以通过7Z打开它,但无法通过Windows资源管理器打开。这是解决方案。这也解决了我的问题,本质上与此相同,只是我创建了一个Helper类,因此ZipArchive不在返回zip文件的同一函数中。 - Ricardo Appleton

3

只需返回流...

private ActionResult CreateZip(IEnumerable files)
{
    if (files.Any())
    {
        MemoryStream zipStream = new MemoryStream();
        using (ZipArchive archive = new ZipArchive(zipStream, ZipArchiveMode.Create, false))
        {
            foreach (var f in files)
            {
               var entry = archive.CreateEntry(f.FileDownloadName, CompressionLevel.Fastest);
               using (var entryStream = entry.Open())
               {
                   entryStream.Write(f.FileContents, 0, f.FileContents.Length);
                   entryStream.Close();
               }
           }

        }

        zipStream.Position = 0;
        return File(zipStream, MediaTypeNames.Application.Zip, "horta.zip");
    }

    return new EmptyResult();
}

1

尝试更改

using (ZipArchive archive = new ZipArchive(zipStream, ZipArchiveMode.Create, false))

using (ZipArchive archive = new ZipArchive(zipStream, ZipArchiveMode.Create, true))

在这种情况下,当归档文件关闭时,它被强制写入流。然而,如果构造函数的leaveOpen参数设置为false,则也会关闭底层流。

0
我遇到了“压缩(已打包)文件夹...无效。”的错误,因为我的条目名字前面带有一个斜杠“/”。一些 zip 解压工具没有问题,但 Windows 的解压工具会出错。我通过将条目名称中的斜杠移除(从“/file.txt”更改为“file.txt”)来解决此问题。

0

当我像示例一样添加了错误的名称时

var fileToZip = "/abc.txt";
ZipArchiveEntry zipFileEntry = zipArchive.CreateEntry(fileToZip);

我遇到了同样的错误。更正文件名后,现在已经没问题了。


0
在返回流之前,尝试在当前实例的System.IO.Compression.ZipArchive类中添加dispose方法,以释放所使用的资源。
 private FileResult CreateZip(IEnumerable<FileContentResult> files)
{
    byte[] retVal = null;

    if (files.Any())
    {
        using (MemoryStream zipStream = new MemoryStream())
        {
            using (ZipArchive archive = new ZipArchive(zipStream, ZipArchiveMode.Create, true))
            {
                foreach (var f in files)
                {
                    var entry = archive.CreateEntry(f.FileDownloadName, CompressionLevel.Fastest);
                    using (BinaryWriter writer = new BinaryWriter(entry.Open()))
                    {                                   
                        writer.Write(f.FileContents, 0, f.FileContents.Length);
                        writer.Close();
                    }
                }

                zipStream.Position = 0;
            }

            archive.Dispose();
            retVal = zipStream.ToArray();
        }
    }

    return File(retVal, MediaTypeNames.Application.Zip, "horta.zip");

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接