5

I am trying to create zip file dynamically on the server that includes photos from online services. But when I try to open the file, WinRar sais the the file is unknown format or damaged.

this is the code:

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();
aikixd
  • 506
  • 5
  • 16
  • 3
    Have you tried this with only one order with one image? Have you tried using a regular `foreach` instead of `Parallel.ForEach`? Is `Package` thread safe on the call to `CreatePart`? It seems like you're going to be writing to the stream from multiple threads, and unless `CreatePart` is threadsafe, then you're going to get a corrupted stream. – Merlyn Morgan-Graham Jul 12 '11 at 21:09
  • I have been doing it now on one image only, so thread safety should not be a problem here – aikixd Jul 12 '11 at 21:16
  • Tried with ordinary foreach. no change – aikixd Jul 12 '11 at 21:20
  • I'm guessing it isn't happy about the uri of the file. – Hans Passant Jul 12 '11 at 21:33
  • I didn't get the idea of how uri should look like. [Here](http://msdn.microsoft.com/en-us/library/ms568068.aspx) it is explained, and as far as i get, I gave a correct uri. But my english is not perfect, so I may be mistaken. – aikixd Jul 12 '11 at 21:41
  • I have just checked Package.Open() with filePath signature, instead of working with a stream and it worked, the created file opened succesfully. I also tried FileStream instead of MemoryStream and it didn't work. both files created by invokes with filePath and FileStream are same size. – aikixd Jul 12 '11 at 22:29
  • How are you receiving the file? Your code just shows the response.Some browser like Chrome add sometimes content when using a download. Instances of ZipPackage are not thread-safe, thus you would need to synchronize access to it when working with multiple threads. – weismat Jul 13 '11 at 03:06
  • For purpose of testing I. Now use an ordinary foreach. I also tried saving the file on the server without download. – aikixd Jul 13 '11 at 08:53

2 Answers2

2

It's all about Pack Uris. We also have been beaten by them. Here is working sample for storing two files in zip. Code just copied from our project with removing some private data.

           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);
Sergey Mirvoda
  • 3,209
  • 2
  • 26
  • 30
  • 1
    You use filePath when invoking the package. You can see by my comments to the question, that this worked for me too. The problem takes place when I use stream as a storage for the data. It looks like in the stream scenario some additional care must be taken. Or this is a bug. – aikixd Jul 13 '11 at 08:49
  • What is the purpose of this: MemoryStream ms = new MemoryStream(data); ms.Write(data, 0, data.Length); You never use ms and never dispose? – Sergey Mirvoda Jul 13 '11 at 09:50
  • Can you rewind stream's Position to 0?. before stream.Read(fileBytes, 0, Convert.ToInt32(stream.Length)); – Sergey Mirvoda Jul 13 '11 at 09:51
  • You right about ms, I forgot to delete it. I was fighting with this so long, that the code became a real mess. I'm talking about stream in the head of the method. What purpose of rewinding, the stream is empty until that string? – aikixd Jul 13 '11 at 10:28
  • when you read from main stream (stream.Read(fileBytes ... ) actually your start reading from current position (stream.Position), I'm not sure who is responsible for rewinding stream. Just check it and rewind to 0. – Sergey Mirvoda Jul 13 '11 at 10:52
  • 1
    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 Jul 13 '11 at 16:12
  • Sergey, your suggestion to set the stream position to zero worked for me in a very similar situation. Thanks – Dan Jul 31 '12 at 09:14
0

I know that this question is a bit old but I had a similar problem. I used

stream.Seek(0, SeekOrigin.Begin);

and it worked

Dave
  • 349
  • 1
  • 15