Search code examples
asp.net-mvcazureazure-storagedotnetzipazure-blob-storage

Not able to properly download files from azure storage and data are lost too when downloading files


I have 2 files saved on Azure blob storage:

  1. Abc.txt
  2. Pqr.docx

Now i want to create zip files of this 2 files and allow user to download.

I have saved this in my database table field like this:

Document
Abc,Pqr

Now when i click on download then i am getting file like below with no data in it and file extension are lost too like below: enter image description here

I want user to get exact file(.txt,.docx) in zip when user download zip file.

This is my code:

public ActionResult DownloadImagefilesAsZip()
{
    string documentUrl = repossitory.GetDocumentsUrlbyId(id);//output:Abc.txt,Pqr.Docx
          if (!string.IsNullOrEmpty(documentUrl))
            {
                string[] str = documentUrl.Split(',');
                if (str.Length > 1)
                {
                    using (ZipFile zip = new ZipFile())
                    {
                        int cnt = 0;
                        foreach (string t in str)
                        {
                            if (!string.IsNullOrEmpty(t))
                            {
                                Stream s = this.GetFileContent(t);
                                zip.AddEntry("File" + cnt, s);

                            }
                            cnt++;
                        }
                        zip.Save(outputStream);
                        outputStream.Position = 0;
                        return File(outputStream, "application/zip", "all.zip");
                    }
                }

}

  public Stream GetFileContent(string fileName)
        {
            CloudBlobContainer container = this.GetCloudBlobContainer();
            CloudBlockBlob blockBlob = container.GetBlockBlobReference(fileName);
            var stream = new MemoryStream();
            blockBlob.DownloadToStream(stream);
            return stream;
        }

 public CloudBlobContainer GetCloudBlobContainer()
        {
            CloudStorageAccount storageAccount = CloudStorageAccount.Parse(ConfigurationManager.AppSettings["StorageConnectionString"].ToString());
            CloudBlobClient blobclient = storageAccount.CreateCloudBlobClient();
            CloudBlobContainer blobcontainer = blobclient.GetContainerReference("Mystorage");
            if (blobcontainer.CreateIfNotExists())
            {
                blobcontainer.SetPermissions(new BlobContainerPermissions { PublicAccess = BlobContainerPublicAccessType.Blob });
            }
            blobcontainer.SetPermissions(new BlobContainerPermissions { PublicAccess = BlobContainerPublicAccessType.Blob });
            return blobcontainer;
        }

I want same file to be downloaded when user download zip file.

Can anybody help me with this??


Solution

  • There are two things I noticed:

    1. Once you read the blob contents in stream, you are not resetting that stream's position to 0. Thus all files in your zip are of zero bytes.
    2. When calling AddEntry, you may want to specify the name of the blob there instead of "File"+cnt.

    Please look at the code below. It's a console app that creates the zip file and writes it on the local file system.

        static void SaveBlobsToZip()
        {
            string[] str = new string[] { "CodePlex.png", "DocumentDB.png" };
            var account = new CloudStorageAccount(new StorageCredentials(accountName, accountKey), true);
            var blobClient = account.CreateCloudBlobClient();
            var container = blobClient.GetContainerReference("images");
            using (var fs = new FileStream("D:\\output.zip", FileMode.Create))
            {
                fs.Position = 0;
                using (var ms1 = new MemoryStream())
                {
                    using (ZipFile zip = new ZipFile())
                    {
                        int cnt = 0;
                        foreach (string t in str)
                        {
                            var ms = new MemoryStream();
                            container.GetBlockBlobReference(t).DownloadToStream(ms);
                            ms.Position = 0;//This was missing from your code
                            zip.AddEntry(t, ms);//You may want to give the name of the blob here.
                            cnt++;
                        }
                        zip.Save(ms1);
                    }
                    ms1.Position = 0;
                    ms1.CopyTo(fs);
                }
            }
        }
    

    UPDATE

    Here's the code in the MVC application (though I am not sure it is the best code :) but it works). I modified your code a little bit.

        public ActionResult DownloadImagefilesAsZip()
        {
            string[] str = new string[] { "CodePlex.png", "DocumentDB.png" }; //repossitory.GetDocumentsUrlbyId(id);//output:Abc.txt,Pqr.Docx
            CloudBlobContainer blobcontainer = GetCloudBlobContainer();// azureStorageUtility.GetCloudBlobContainer();
            MemoryStream ms1 = new MemoryStream();
            using (ZipFile zip = new ZipFile())
            {
                int cnt = 0;
                foreach (string t in str)
                {
                    var ms = new MemoryStream();
                    CloudBlockBlob blockBlob = blobcontainer.GetBlockBlobReference(t);
                    blockBlob.DownloadToStream(ms);
                    ms.Position = 0;//This was missing from your code
                    zip.AddEntry(t, ms);//You may want to give the name of the blob here.
                    cnt++;
                }
                zip.Save(ms1);
            }
            ms1.Position = 0;
            return File(ms1, "application/zip", "all.zip");
        }