Generate a Zip file from Azure Blob Storage Files

You might have a task that pops up where you need to generate a zip file from a number of files in your Azure blob storage account. For 1 or 2 files, this may not be a problem but for 20-2000, you might want to find a way to automate this.

One option is to zip the files directly to the output stream using the blob streams. If you do this it still means that you are routing the stream through the web server.

To begin you will want a way work with zip easily. I used the latest version of ICSharpZipLib and make sure you pull in the Azure Storage library.

We'll setup our Storage Service which will read from the stream and get a reference to the blob we are currently looping through.

public void ReadToStream(IFileIdentifier file, Stream stream, StorageType storageType = StorageType.Stored, ITenant overrideTenant = null)
{
    var reference = GetBlobReference(file, storageType, overrideTenant);
    reference.DownloadToStream(stream);
}


private CloudBlockBlob GetBlobReference(IFileIdentifier file, StorageType storageType = StorageType.Stored, ITenant overrideTenant = null)
{
    var filepath = GetFilePath(file, storageType);
    var container = GetTenantContainer(overrideTenant);
    return container.GetBlockBlobReference(filepath);
}
1
2
3
4
5
6
7
8
9
10
11
12
13

Now we have one method that takes in a response stream and loops through all the files to generate our zip file.

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();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

Another "quick and dirty" MVC sample can be found below. This sample specifies the file names and the zip file name.

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;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25