ZipArchive 创建无效的 ZIP 文件

Posted

技术标签:

【中文标题】ZipArchive 创建无效的 ZIP 文件【英文标题】:ZipArchive creates invalid ZIP file 【发布时间】:2012-09-10 08:21:44 【问题描述】:

我正在尝试使用一个条目从代码创建一个新的 ZIP 包,并将 ZIP 包保存到一个文件中。我正在尝试使用 System.IO.Compression.ZipArchive 类来实现这一点。我正在使用以下代码创建 ZIP 包:

using (MemoryStream zipStream = new MemoryStream())

    using (ZipArchive zip = new ZipArchive(zipStream, ZipArchiveMode.Create))
    
        var entry = zip.CreateEntry("test.txt");
        using (StreamWriter sw = new StreamWriter(entry.Open()))
        
            sw.WriteLine(
                "Etiam eros nunc, hendrerit nec malesuada vitae, pretium at ligula.");
        

然后我将 ZIP 保存到 WinRT 中的文件:

        var file = await Windows.Storage.ApplicationData.Current.LocalFolder.CreateFileAsync("test.zip", CreationCollisionOption.ReplaceExisting);
        zipStream.Position = 0;
        using (Stream s = await file.OpenStreamForWriteAsync())
        
            zipStream.CopyTo(s);
        

或者在普通的 .NET 4.5 中:

        using (FileStream fs = new FileStream(@"C:\Temp\test.zip", FileMode.Create))
        
            zipStream.Position = 0;
            zipStream.CopyTo(fs);
        

但是,我无法在 Windows 资源管理器、WinRAR 等中打开生成的文件(我检查了生成文件的大小是否与 zipStream 的长度匹配,因此流本身已正确保存到文件中。 ) 我做错了什么还是 ZipArchive 类有问题?

【问题讨论】:

如果您要添加二进制数据而不是字符串,这是一个很好的示例:***.com/questions/48927574/… 【参考方案1】:

我在我的代码中发现了——事后看来,很明显——错误。 ZipArchive 必须被disposed 以使其将其内容写入其底层流。所以我不得不在 ZipArchive 的 using 块结束后将流保存到一个文件中。 并且将其构造函数的 leaveOpen 参数设置为 true 很重要,以使其不会关闭底层流。所以这是完整的工作解决方案:

using (MemoryStream zipStream = new MemoryStream())

    using (ZipArchive zip = new ZipArchive(zipStream, ZipArchiveMode.Create, true))
    
        var entry = zip.CreateEntry("test.txt");
        using (StreamWriter sw = new StreamWriter(entry.Open()))
        
            sw.WriteLine(
                "Etiam eros nunc, hendrerit nec malesuada vitae, pretium at ligula.");
        
    

    var file = await Windows.Storage.ApplicationData.Current.LocalFolder.CreateFileAsync(
        "test.zip",
        CreationCollisionOption.ReplaceExisting);

    zipStream.Position = 0;
    using (Stream s = await file.OpenStreamForWriteAsync())
    
        zipStream.CopyTo(s);
    

【讨论】:

我收到错误消息“当前上下文中不存在名称‘Windows’”。我该如何纠正这个问题? Michael,这是构建错误,对吧?您是在开发 Windows 应用商店应用程序,还是普通的 .NET 应用程序?在普通的 .NET 应用程序中,您应该使用 FileStream 类,而不是 Windows.Storage 命名空间中的存储类。您可以在问题中看到一个示例。【参考方案2】:

在您的所有 Stream 对象上,您必须从开头倒回流,以便其他应用程序使用 .Seek 方法正确读取它们。

例子:

zipStream.Seek(0, SeekOrigin.Begin);

【讨论】:

这没有帮助。这就是为什么我将 Position 属性设置为 0,我猜这样可以达到相同的效果。 zip 库必须有自己的 SaveAs 方法,尝试研究一下,在项目 peage 上也有各种不同保存场景的文档。 嗯,ZipArchive 类 (msdn.microsoft.com/en-us/library/…) 没有 SaveAs 方法。 “项目页面”是什么意思? 对不起,我以为是第 3 方库。 @Freeman 这在我的案例中起到了作用。谢谢!【参考方案3】:
// Create file "archive.zip" in current directory use it as destination for ZIP archive
using (var zipArchive = new ZipArchive(File.OpenWrite("archive.zip"), 
ZipArchiveMode.Create))

    // Create entry inside ZIP archive with name "test.txt"
    using (var entry = zipArchive.CreateEntry("test.txt").Open())
    
        // Copy content from current directory file "test.txt" into created ZIP entry 
        using (var file = File.OpenRead("test.txt"))
        
            file.CopyTo(entry);
        
    
    

结果,您将获得包含单个条目文件“test.txt”的存档“archive.zip”。 您需要这种级联的using 来避免与已释放资源的任何交互。

【讨论】:

请为您的代码添加解释,以及它如何解决问题。 @DouglasGaskell 完成。可能上面的代码没有直接回答问题,而是展示了如何使用 ZipArchive 压缩一些文件【参考方案4】:

您可以遵循相同的想法,只是以相反的顺序,使用类似源的文件流。我做了下面的表格,文件正常打开了:

string fileFormat = ".zip"; // any format
string filename = "teste" + fileformat;

StorageFile zipFile = await Windows.Storage.ApplicationData.Current.LocalFolder.CreateFileAsync(filename,CreationCollisionOption.ReplaceExisting);

using (Stream zipStream = await zipFile.OpenStreamForWriteAsync())

   using (ZipArchive archive = new ZipArchive(zipStream, ZipArchiveMode.Create))
        //Include your content here
   

【讨论】:

【参考方案5】:

完整的代码如下所示:

var file = await Windows.Storage.ApplicationData.Current.LocalFolder.CreateFileAsync("test.zip",CreationCollisionOption.ReplaceExisting);
using (Stream zipStream = await zipFile.OpenStreamForWriteAsync())

    using (ZipArchive zip = new ZipArchive(zipStream, ZipArchiveMode.Create, true))
    
        var entry = zip.CreateEntry("test.txt");
        using (StreamWriter sw = new StreamWriter(entry.Open()))
        
            sw.WriteLine("Etiam eros nunc, hendrerit nec malesuada vitae, pretium at ligula.");
        
       

【讨论】:

【参考方案6】:

在我的情况下,问题是当 zip 文件任务完成时我没有处理 "zipArchive"。即使我正在刷新并关闭所有流。

因此,请按照上述答案中的建议使用 using

using (var zipArchive = new ZipArchive(File.OpenWrite("archive.zip"), 
ZipArchiveMode.Create))

或者在任务结束时释放变量...

zipArchive.Dispose();

【讨论】:

以上是关于ZipArchive 创建无效的 ZIP 文件的主要内容,如果未能解决你的问题,请参考以下文章

我使用 PHP 的 ZipArchive 创建的 .zip 存档在 Windows 10 上已损坏/无效

php ZipArchive 多文件打包下载

PHP:使用 ZipArchive 创建 zip 文件时出现错误 (500)

Laravel 8,为公共文件夹中的所有图像创建 zip 文件。面对文件错误:ZipArchive::addFile(): Invalid or uninitialized Zip object

php ZipArchive 压缩整个文件夹 - 自带ZipArchive类 - PHP递归创建目录压缩包

如何在 Laravel 测试中模拟类的属性