使用System.IO.Packaging在C#中创建zip文件

5

我正在尝试在服务器上动态创建包含来自在线服务的照片的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();

3
你尝试过只使用一个订单和一张图片吗?你尝试过使用常规的 foreach 而不是 Parallel.ForEach 吗?在调用 CreatePart 时,Package 是否线程安全?看起来你将从多个线程对流进行写入,除非 CreatePart 是线程安全的,否则你会得到损坏的流。 - Merlyn Morgan-Graham
我现在只在一个图像上进行操作,所以线程安全应该不是问题。 - aikixd
尝试使用普通的foreach,但没有变化。 - aikixd
我不清楚uri应该长成什么样子。这里有解释,据我理解,我给了正确的uri。但我的英语并不完美,所以可能会出错。 - aikixd
我刚刚使用带有文件路径签名的Package.Open()进行了检查,而不是使用流进行操作,并且它能够成功打开所创建的文件。我也尝试了FileStream而不是MemoryStream,但它没有起作用。使用filePath和FileStream调用创建的两个文件大小相同。 - aikixd
显示剩余3条评论
2个回答

2

这涉及到包URI。我们也被它们击败过。下面是一个存储两个文件在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);

1
在调用包时使用filePath。从我的评论中可以看出,这对我也起作用了。问题发生在我使用流作为数据存储时。似乎在流场景下需要采取一些额外的注意事项。或者这是一个错误。 - aikixd
这是什么目的:MemoryStream ms = new MemoryStream(data); ms.Write(data, 0, data.Length); 你从未使用ms,也从未处理它? - Sergey Mirvoda
你说得对,我忘了把它删除。我一直在解决这个问题,所以代码变得很混乱。我是在谈论方法头部的流。倒回的目的是什么,直到那个字符串之前,流都是空的? - aikixd
当你从主流中读取(stream.Read(fileBytes ...)),实际上你是从当前位置(stream.Position)开始读取的,我不确定谁负责倒回流。只需检查并倒回到0。 - Sergey Mirvoda
1
你能把这段代码改成这样吗? byte[] fileBytes = new byte[stream.Length]; stream.Position = 0; stream.Read(fileBytes, 0, Convert.ToInt32(stream.Length)); 原文: can you change this code byte[] fileBytes = new byte[stream.Length]; stream.Read(fileBytes, 0, Convert.ToInt32(stream.Length)); to this: byte[] fileBytes = new byte[stream.Length]; stream.Position = 0; stream.Read(fileBytes, 0, Convert.ToInt32(stream.Length)); - Sergey Mirvoda
显示剩余2条评论

0

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

stream.Seek(0, SeekOrigin.Begin);

它运行成功了


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