4

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??

I Love Stackoverflow
  • 6,738
  • 20
  • 97
  • 216

3 Answers3

2

I'm not a web dev, but hopefully this will help. This snippet of code is in a method where I download a list of blobs into a zip file archive using a stream. The list of files had the slashes in all directions, so there's code in here to fix this, and to make sure I'm getting the blob reference with the right text (no URL, and no opening slash if the blob is in a "folder").

I suspect your problem is not using a memory stream or a binary writer. Specificity helps sometimes. Good luck.

using (ZipArchive zipFile = ZipFile.Open(outputZipFileName, ZipArchiveMode.Create))
{
    foreach (string oneFile in listOfFiles)
    {
        //Need the filename, complete with relative path. Make it like a file name on disk, with backwards slashes.
        //Also must be relative, so can't start with a slash. Remove if found.
        string filenameInArchive = oneFile.Replace(@"/", @"\");
        if (filenameInArchive.Substring(0, 1) == @"\")
            filenameInArchive = filenameInArchive.Substring(1, filenameInArchive.Length - 1);

        //blob needs slashes in opposite direction
        string blobFile = oneFile.Replace(@"\", @"/");

        //take first slash off of the (folder + file name) to access it directly in blob storage
        if (blobFile.Substring(0, 1) == @"/")
            blobFile = oneFile.Substring(1, oneFile.Length - 1);

        var cloudBlockBlob = this.BlobStorageSource.GetBlobRef(blobFile);
        if (!cloudBlockBlob.Exists()) //checking just in case
        {
            //go to the next file
            //should probably trace log this 
            //add the file name with the fixed slashes rather than the raw, messed-up one
            //  so anyone looking at the list of files not found doesn't think it's because
            //  the slashes are different
            filesNotFound.Add(blobFile);
        }
        else
        {
            //blob listing has files with forward slashes; that's what the zip file requires
            //also, first character should not be a slash (removed it above)

            ZipArchiveEntry newEntry = zipFile.CreateEntry(filenameInArchive, CompressionLevel.Optimal);

            using (MemoryStream ms = new MemoryStream())
            {
                //download the blob to a memory stream
                cloudBlockBlob.DownloadToStream(ms);

                //write to the newEntry using a BinaryWriter and copying it 4k at a time
                using (BinaryWriter entry = new BinaryWriter(newEntry.Open()))
                {
                    //reset the memory stream's position to 0 and copy it to the zip stream in 4k chunks
                    //this keeps the process from taking up a ton of memory
                    ms.Position = 0;
                    byte[] buffer = new byte[4096];

                    bool copying = true;
                    while (copying)
                    {
                        int bytesRead = ms.Read(buffer, 0, buffer.Length);
                        if (bytesRead > 0)
                        {
                            entry.Write(buffer, 0, bytesRead);
                        }
                        else
                        {
                            entry.Flush();
                            copying = false;
                        }
                    }
                }//end using for BinaryWriter

            }//end using for MemoryStream

        }//if file exists in blob storage

    }//end foreach file

} //end of using ZipFileArchive
  • can you please tell me how do i integrate this with my mvc controller method because main problem is with my controller like how would be my controller method? – I Love Stackoverflow Oct 26 '15 at 08:45
1

I have seen people using ICSharpZip library, take a look at this piece of code

public void ZipFilesToResponse(HttpResponseBase response, IEnumerable<Asset> files, string zipFileName)
    {
        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);
            response.ContentType = "application/octet-stream";

            foreach (var file in files)
            {
                var entry = new ZipEntry(file.FilenameSlug())
                {
                    DateTime = DateTime.Now,
                    Size = file.Filesize
                };
                zipOutputStream.PutNextEntry(entry);
                storageService.ReadToStream(file, zipOutputStream);
                response.Flush();
                if (!response.IsClientConnected)
                {
                   break;
                }
            }
            zipOutputStream.Finish();
            zipOutputStream.Close();
        }
        response.End();
    }

Taken from here generate a Zip file from azure blob storage files

Community
  • 1
  • 1
Brij Raj Singh - MSFT
  • 4,903
  • 7
  • 36
  • 55
1

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");
    }
Gaurav Mantri
  • 128,066
  • 12
  • 206
  • 241