【问题标题】:Unable to create zip file using Ionic.Zip无法使用 Ionic.Zip 创建 zip 文件
【发布时间】:2021-03-24 16:10:48
【问题描述】:

我不确定我在哪里做错了什么,但我使用 DotNetZip 库创建的 zip 正在创建一个内容为空白的 zip 文件。或者 zip 中的文件大小显示为 0Kb 并且无法打开。

代码:

public static async Task DotNetZipFileAsync(MemoryStream stream, string bucket, List<List<string>> pdfFileSet, IAmazonS3 s3Client)
        {
           
            using Ionic.Zip.ZipFile zip = new ZipFile();
            foreach (var pdfFile in pdfFileSet)
            {
                foreach (var file in pdfFile)
                {
                    GetObjectRequest request = new GetObjectRequest
                    {
                        BucketName = bucket,
                        Key = file
                    };
                    
                    using GetObjectResponse response = await s3Client.GetObjectAsync(request);
                    using Stream responseStream = response.ResponseStream;
                    ZipEntry zipEntry = zip.AddEntry(file.Split('/')[^1], responseStream);
                    await responseStream.CopyToAsync(stream);
                }
            }
            zip.Save(stream);
            stream.Seek(0,SeekOrigin.Begin);
            await stream.CopyToAsync(new FileStream(@"C:\LocalRepo\Temp.zip", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite));
        }
    }
}

【问题讨论】:

  • 这个参数stream有什么用?将响应复制到此流可能会消耗整个下载的内容。
  • 就是将内容写入stream,然后从stream中创建一个zip文件。
  • 不,看看documentationAddEntry 的第二个参数应该是在调用zip.Save() 时可读的流。因为您之前正在处理 responseStream,所以它不起作用。
  • 没听懂,你的意思是 responseStream 正在处理,在哪里?
  • 在其范围的末尾(}

标签: c# zipfile dotnetzip


【解决方案1】:

你的代码至少有两个问题:

  1. 读取流完全被await responseStream.CopyToAsync(stream) 消耗。你可以回退 responseStream 来解决这个问题,但是将数据保存到内存流中是完全没用的。

  2. 响应流在调用zip.Save 之前被释放。

您可以做的:在调用 Save 之前保持流打开并在之后处理它们。正如 Alexey Rumyantsev 发现的(参见 cmets),GetObjectResponse 对象也需要保留,直到 ZIP 文件被保存。

using Ionic.Zip.ZipFile zip = new ZipFile();
var disposables = List<IDisposable>();
try
{
    foreach (var pdfFile in pdfFileSet)
    {
        foreach (var file in pdfFile)
        {
            GetObjectRequest request = new GetObjectRequest
            {
                BucketName = bucket,
                Key = file
            };
            
            var response = await s3Client.GetObjectAsync(request);
            disposables.Add(response);
            var responseStream = response.ResponseStream;
            disposables.Add(responseStream);
            ZipEntry zipEntry = zip.AddEntry(file.Split('/')[^1], responseStream);
        }
    }
    using var fileStream = new FileStream(@"C:\LocalRepo\Temp.zip", FileMode.Create, FileAccess.Write);        
    zip.Save(fileStream);
}
finally
{
    foreach (var disposable in disposables)
    {
        disposable.Dispose();
    }
}

documentation 提供了一些关于如何使其更智能的提示。

【讨论】:

  • 这段代码可能有另一个问题,即文件非常大,所有流同时打开以保持连接处于活动状态,但随后它们会被一一处理,如果读取一个流将花费很多时间其他可以超时。但这超出了这个问题的范围,并导致了更复杂的解决方案。
  • @Klaus 感谢您的帮助,但这仍然无法正常工作。现在创建的 zip 文件给出了 Invalid file 的错误
  • 我想你必须自己处理FileStream,把它包裹在using中。
  • 也许你的文件没有被之前的尝试截断尝试使用FileMode.Create
  • @Aniruddha 您的解决方案内存效率低下,如果您使用大文件,过度分配可能会变得非常大,如果您对此感到满意,那么只需使用您的解决方案。但是,如果您需要最佳解决方案,那么我会看到两个结果,1 - 该库存在处理流的错误并且仅适用于字节数组,在这种情况下,您的解决方案是唯一可能的,2 - 您在开始之前面临响应超时的问题处理他们的流,我在第一条评论中提到过,在这种情况下,您可以使用 AddEntry 的另一个重载,您可以在其中传递打开和关闭流的操作。
【解决方案2】:
public static async Task DotNetZipFileAsync(string bucket, List<List<string>> pdfFileSet, IAmazonS3 s3Client)
{
    int read;
    using Ionic.Zip.ZipFile zip = new ZipFile();
    byte[] buffer = new byte[16 * 1024];
    
    foreach (var pdfFile in pdfFileSet)
    {
        foreach (var file in pdfFile)
        {
            GetObjectRequest request = new GetObjectRequest
            {
                BucketName = bucket,
                Key = file
            };

            using GetObjectResponse response = await s3Client.GetObjectAsync(request);
            using Stream responseStream = response.ResponseStream;
            using (MemoryStream ms = new MemoryStream())
            {
                while ((read = responseStream.Read(buffer, 0, buffer.Length)) > 0)
                {
                    ms.Write(buffer, 0, read);
                }
                zip.AddEntry(file.Split('/')[^1], ms.ToArray());
            }
        }
    }
    using var fileStream = new FileStream(@"C:\LocalRepo\Temp.zip", FileMode.Create, FileAccess.Write);
    zip.Save(fileStream);
}

【讨论】:

    猜你喜欢
    • 2013-02-23
    • 2016-11-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多