使用 System.IO.Packaging 在 c# 中创建 zip

Posted

技术标签:

【中文标题】使用 System.IO.Packaging 在 c# 中创建 zip【英文标题】:Creating zip in c# with System.IO.Packaging 【发布时间】:2011-07-12 21:00:31 【问题描述】:

我正在尝试在服务器上动态创建包含来自在线服务的照片的 zip 文件。但是当我尝试打开文件时,WinRar 说文件格式未知或损坏。

这是代码:

MemoryStream stream = new MemoryStream();
Package package = ZipPackage.Open(stream, FileMode.Create, FileAccess.ReadWrite);

foreach (Order o in orders)

    o.GetImages();

    Parallel.ForEach(o.Images, (Dictionary<string, object> image) =>
    
        string imageId = (string)image["ID"];
        int orderId = (int)image["OrderId"];
        string fileName = (string)image["FileName"];
        string url = (string)image["URL"];

        if (string.IsNullOrEmpty(fileName))
        
            System.Net.WebClient wc = new System.Net.WebClient();
            byte[] data = wc.DownloadData(url);
            MemoryStream ms = new MemoryStream(data);
            ms.Write(data, 0, data.Length);

            string ext;

            switch(wc.ResponseHeaders["Content-Type"])
            
                case "image/jpeg":
                case "image/jpg":
                ext = "jpg";
                    break;

                case "image/png":
                    ext = "png";
                    break;

                case "image/gif":
                    ext = "gif";
                    break;

                default :
                    ext = "un";
                    break;
            

            Uri uri = new Uri("/" + orderId.ToString() + "/" + imageId + "." + ext, UriKind.Relative);

            var part = package.CreatePart(uri, wc.ResponseHeaders["Content-Type"], CompressionOption.NotCompressed);

            var pstream = part.GetStream(FileMode.Open, FileAccess.ReadWrite);

            pstream.Write(data, 0, data.Length);

            var rel = package.CreateRelationship(part.Uri, TargetMode.Internal, "http://example.com/AlbumImage");


        
    );


package.Flush();

byte[] fileBytes = new byte[stream.Length];
stream.Read(fileBytes, 0, Convert.ToInt32(stream.Length));

Response.Clear();
Response.AddHeader("Content-Disposition", "attachment; filename=Orders.zip");
Response.AddHeader("Content-Length", stream.Length.ToString());
Response.ContentType = "application/octet-stream";
Response.BinaryWrite(fileBytes);
Response.End();

【问题讨论】:

您是否尝试过仅使用一张图片的订单?您是否尝试过使用普通的foreach 而不是Parallel.ForEach?调用CreatePartPackage 线程安全吗?看起来你要从多个线程写入流,除非CreatePart 是线程安全的,否则你会得到一个损坏的流。 我现在只在一张图片上做,所以线程安全在这里应该不是问题 用普通的foreach试过了。没有变化 我猜它对文件的 uri 不满意。 我不知道 uri 应该是什么样子。 Here 已解释,据我所知,我给出了正确的 uri。但是我的英语不是很完美,所以我可能弄错了。 【参考方案1】:

这都是关于 Pack Uris 的。我们也被他们打过。这是在 zip 中存储两个文件的工作示例。刚刚从我们的项目中复制的代码,删除了一些私有数据。

           var dataFilePath = Path.GetFileName(dataFileName);
           var dataFileUri = PackUriHelper.CreatePartUri(new Uri(dataFilePath, UriKind.Relative));
            // Create the Package
            using (var package = Package.Open(filePath, FileMode.Create))
            
                // Add the diagram view part to the Package
                var pkgPart = package.CreatePart(dataFileUri, MediaTypeNames.Application.Octet);
                var pkgStream = pkgPart.GetStream();

                // Copy the data to the model view part
                // diagramFileName Encoding.Default.GetBytes(text)
                using (var modelStream = new FileStream(dataFileName, FileMode.Open, FileAccess.Read))
                
                    const int bufSize = 0x1000;
                    var buf = new byte[bufSize];
                    int bytesRead;
                    while ((bytesRead = modelStream.Read(buf, 0, bufSize)) > 0)
                    
                        pkgStream.Write(buf, 0, bytesRead);
                    
                

                // Add a context Part to the Package
                var pkgPartContext = package.CreatePart(ctxUri, MediaTypeNames.Application.Octet);
                var ctxPkgStream = pkgPartContext.GetStream();

                // Copy the data to the context part
                using (var ctxStream = new FileStream(ctxFileName, FileMode.Open, FileAccess.Read))
                
                    const int bufSize = 0x1000;
                    var buf = new byte[bufSize];
                    int bytesRead;
                    while ((bytesRead = ctxStream.Read(buf, 0, bufSize)) > 0)
                    
                        ctxPkgStream.Write(buf, 0, bytesRead);
                    
                
            

            // remove tmp files
            File.Delete(ctxFileName);
            File.Delete(dataFileName);

【讨论】:

在调用包时使用 filePath。您可以通过我的 cmets 看到这个问题,这对我也有用。当我使用流作为数据的存储时,就会出现问题。看起来在流场景中必须采取一些额外的措施。或者这是一个错误。 这样做的目的是什么:MemoryStream ms = new MemoryStream(data); ms.Write(data, 0, data.Length);你从不使用 ms 也从不丢弃? 你能把流的位置倒回到 0 吗?之前 stream.Read(fileBytes, 0, Convert.ToInt32(stream.Length)); 你说得对,我忘了删除它。我为此奋斗了很长时间,以至于代码变得一团糟。我说的是方法头部的流。倒带的目的是什么,直到那个字符串流是空的? 你能改变这个代码吗 byte[] fileBytes = new byte[stream.Length]; stream.Read(fileBytes, 0, Convert.ToInt32(stream.Length));为此: byte[] fileBytes = new byte[stream.Length]; stream.Position = 0; stream.Read(fileBytes, 0, Convert.ToInt32(stream.Length));【参考方案2】:

我知道这个问题有点老了,但我遇到了类似的问题。我用过

stream.Seek(0, SeekOrigin.Begin);

成功了

【讨论】:

以上是关于使用 System.IO.Packaging 在 c# 中创建 zip的主要内容,如果未能解决你的问题,请参考以下文章

使用 System.IO.Packaging 生成 ZIP 文件

使用 System.IO.Packaging 在 c# 中创建 zip

System.IO.Packaging无法使用,需引用WindowsBase.dll

在 c# 中使用 System.IO.Packaging 对 .xlsx 文件进行数字签名

从不可搜索的流中打开 System.IO.Packaging.Package

开放包装约定/ System.IO.Packaging 教程 [关闭]