3

I'm using this solution to download files, zip and send to user, (Azure blobs).

This is what I have came up with so far:

    public void DownloadImages(DownloadImagesModel model)
    {
        if (ModelState.IsValid && model.ImageIDs != null && model.ImageIDs.Count > 0)
        {
            var currentUser = _claimService.GetCurrentClaimsIdentity(TimeZoneExtensions.GetCurrentDate());

            var images = _fileService.GetImages(currentUser.CompanyID, model.ImageIDs).ToList();

            if (images != null && images.Count > 0)
            {
                string imageContainerURL = _fileService.GetImageContainerURL(currentUser.CompanyID) + currentUser.ImageContainerToken;

                using (var zipOutputStream = new ZipOutputStream(Response.OutputStream))
                {
                    zipOutputStream.SetLevel(0); // 0 - store only to 9 - means best compression
                    Response.BufferOutput = false;
                    Response.AddHeader("Content-Disposition", "attachment; filename=" + "zipFileName.rar");
                    Response.ContentType = "application/octet-stream";

                    foreach (var image in images)
                    {
                        if (image.ImageLinks != null)
                        {
                            List<ImageLinkModel> imageLinks = new List<ImageLinkModel>();

                            if (model.FormatID == 0)
                                imageLinks = image.ImageLinks.ToList();
                            else
                                imageLinks = image.ImageLinks.Where(m => m.FormatID == model.FormatID).ToList();

                            if (imageLinks != null && imageLinks.Count > 0)
                            {
                                //Every image has multiple blobs for different sizes
                                foreach (var link in imageLinks)
                                {
                                    CloudBlockBlob blob = _fileService.GetBlob(imageContainerURL, link.BlobName);

                                    var entry = new ZipEntry(image.ImageName)
                                    {
                                        DateTime = TimeZoneExtensions.GetCurrentDate(),
                                        Size = blob.Properties.Length
                                    };

                                    zipOutputStream.PutNextEntry(entry);

                                    //Download
                                    blob.DownloadToStream(zipOutputStream);

                                    Response.Flush();

                                    if (!Response.IsClientConnected)
                                    {
                                        break;
                                    }
                                }
                            }
                        }
                    }

                    zipOutputStream.Finish();
                    zipOutputStream.Close();
                }

                Response.End();
            }
        }
    }

When I open the downloaded zip file I get:

Unexpected end of archive.

The zip should contain three files, but only contains one. The size is also 0..

What have I missed?

Edit 1

Should I specify .rar type when naming?

Don't I need to set position for stream?

Have I positioned Flush, Finish, Close and End functions correctly?

Edit 2

This code should work!?

    public ActionResult DownloadImages(DownloadImagesModel model)
    {
        if (ModelState.IsValid && model.ImageIDs != null && model.ImageIDs.Count > 0)
        {
            var currentUser = _claimService.GetCurrentClaimsIdentity(TimeZoneExtensions.GetCurrentDate());

            var images = _fileService.GetImages(currentUser.CompanyID, model.ImageIDs).ToList();

            if (images != null && images.Count > 0)
            {
                var cloudStorageAccount = new CloudStorageAccount(new StorageCredentials("name", "pw"), true);
                var container = cloudStorageAccount.CreateCloudBlobClient().GetContainerReference("images-1");

                using (var zipOutputStream = new ZipOutputStream(Response.OutputStream))
                {
                    foreach (var image in images)
                    {
                        if (image.ImageLinks != null)
                        {
                            List<ImageLinkModel> imageLinks = new List<ImageLinkModel>();

                            if (model.FormatID == 0)
                                imageLinks = image.ImageLinks.ToList();
                            else
                                imageLinks = image.ImageLinks.Where(m => m.FormatID == model.FormatID).ToList();

                            if (imageLinks != null && imageLinks.Count > 0)
                            {
                                zipOutputStream.SetLevel(0);

                                var blob = container.GetBlockBlobReference(imageLinks.First().BlobName);

                                var entry = new ZipEntry(imageLinks.First().BlobName);
                                zipOutputStream.PutNextEntry(entry);

                                //Download
                                blob.DownloadToStream(zipOutputStream);
                            }
                        }
                    }

                    zipOutputStream.Finish();
                    zipOutputStream.Close();
                }

                Response.BufferOutput = false;
                Response.AddHeader("Content-Disposition", "attachment; filename=" + "zipFileName.zip");
                Response.ContentType = "application/octet-stream";
                Response.Flush();
                Response.End();
                return null;
            }
        }

        return null;
    }
Community
  • 1
  • 1
Reft
  • 2,333
  • 5
  • 36
  • 64
  • Why are you flushing the response after reading the 1st blob? Shouldn't you be doing this after all blobs have been read? – Gaurav Mantri Feb 29 '16 at 13:42
  • That's a good question. I actually just followed the other guys implementation. I moved the flushing to just before the .finish and close function but it still gives me the same problem.. – Reft Feb 29 '16 at 13:44
  • Can you try by putting `blob.DownloadToStream(zipOutputStream);` before `zipOutputStream.PutNextEntry(entry);` and also flush the response after `zipOutputStream.Close()` and see if that makes any difference? – Gaurav Mantri Feb 29 '16 at 13:51
  • Exception: Additional information: No open entry. when trying to download to stream, (blob.DownloadToStream(zipOutputStream)) – Reft Feb 29 '16 at 13:54
  • I thought when the stream was read in, its position isn't reset to 0... It's at the last bit of the stream... Have you tried setting the position of the stream back to 0 (before you try to read the stream)? – Rikon Feb 29 '16 at 13:55
  • When I try to set position with: zipOutputStream.Position = 0; It gives me the exception: Additional information: Position property not supported. Also, I forgot to tell you, I'm using SharpZipLib for zipping. – Reft Feb 29 '16 at 14:00

2 Answers2

7

I was able to create a zip file and download it using the code below:

    public ActionResult Download()
    {
        var cloudStorageAccount = new CloudStorageAccount(new StorageCredentials("account-name", "account-key"), true);
        var container = cloudStorageAccount.CreateCloudBlobClient().GetContainerReference("test");
        var blobFileNames = new string[] { "file1.png", "file2.png", "file3.png", "file4.png" };
        using (var zipOutputStream = new ZipOutputStream(Response.OutputStream))
        {
            foreach (var blobFileName in blobFileNames)
            {
                zipOutputStream.SetLevel(0);
                var blob = container.GetBlockBlobReference(blobFileName);
                var entry = new ZipEntry(blobFileName);
                zipOutputStream.PutNextEntry(entry);
                blob.DownloadToStream(zipOutputStream);
            }
            zipOutputStream.Finish();
            zipOutputStream.Close();
        }
        Response.BufferOutput = false;
        Response.AddHeader("Content-Disposition", "attachment; filename=" + "zipFileName.zip");
        Response.ContentType = "application/octet-stream";
        Response.Flush();
        Response.End();
        return null;
    }
Gaurav Mantri
  • 128,066
  • 12
  • 206
  • 241
  • I've exactly the same code as you, and my zip is still corrupted. When I debug and hover over ZipOutputStream I got a couple of errors. Do you also have them? Screenshot: http://imgur.com/Vy0aq5N – Reft Feb 29 '16 at 15:45
  • I have updated my question with new code.. As you can see I have done exactly as you have shown above.. Why must the gods corrupt my zip files? – Reft Feb 29 '16 at 16:27
  • This is really weird. The code is identical and should work. You're still getting the same error as you were getting initially? – Gaurav Mantri Feb 29 '16 at 16:48
  • Any specific reason why `zipOutputStream.SetLevel(0);` is inside the loop? – TKharaishvili Mar 26 '17 at 21:24
1

My blob container was private, that is why the files were never downloaded.

I actually thought my container was public but it was not untill I explored my containers in Azure Storage Explorer that I saw that the container was private.

Reft
  • 2,333
  • 5
  • 36
  • 64