Our ASP.NET MVC 3 application is running on Azure and using Blob as file storage. I have the upload part figured out.
The View is going to have the File Name, which, when clicked will prompt the file download screen to appear.
Can anyone tell me how to go about doing this?
Two options really... the first is to just redirect the user to the blob directly (if the blobs are in a public container). That would look a bit like:
return Redirect(container.GetBlobReference(name).Uri.AbsoluteUri);
If the blob is in a private container, you could either use a Shared Access Signature and do redirection like the previous example, or you could read the blob in your controller action and push it down to the client as a download:
Response.AddHeader("Content-Disposition", "attachment; filename=" + name); // force download container.GetBlobReference(name).DownloadToStream(Response.OutputStream); return new EmptyResult();
Here's a resumable version (useful for large files or allowing seek in video or audio playback) of private blob access:
public class AzureBlobStream : ActionResult { private string filename, containerName; public AzureBlobStream(string containerName, string filename) { this.containerName = containerName; this.filename = filename; this.contentType = contentType; } public override void ExecuteResult(ControllerContext context) { var response = context.HttpContext.Response; var request = context.HttpContext.Request; var connectionString = ConfigurationManager.ConnectionStrings["Storage"].ConnectionString; var account = CloudStorageAccount.Parse(connectionString); var client = account.CreateCloudBlobClient(); var container = client.GetContainerReference(containerName); var blob = container.GetBlockBlobReference(filename); blob.FetchAttributes(); var fileLength = blob.Properties.Length; var fileExists = fileLength > 0; var etag = blob.Properties.ETag; var responseLength = fileLength; var buffer = new byte[4096]; var startIndex = 0; //if the "If-Match" exists and is different to etag (or is equal to any "*" with no resource) then return 412 precondition failed if (request.Headers["If-Match"] == "*" && !fileExists || request.Headers["If-Match"] != null && request.Headers["If-Match"] != "*" && request.Headers["If-Match"] != etag) { response.StatusCode = (int)HttpStatusCode.PreconditionFailed; return; } if (!fileExists) { response.StatusCode = (int)HttpStatusCode.NotFound; return; } if (request.Headers["If-None-Match"] == etag) { response.StatusCode = (int)HttpStatusCode.NotModified; return; } if (request.Headers["Range"] != null && (request.Headers["If-Range"] == null || request.Headers["IF-Range"] == etag)) { var match = Regex.Match(request.Headers["Range"], @"bytes=(\d*)-(\d*)"); startIndex = Util.Parse<int>(match.Groups[1].Value); responseLength = (Util.Parse<int?>(match.Groups[2].Value) + 1 ?? fileLength) - startIndex; response.StatusCode = (int)HttpStatusCode.PartialContent; response.Headers["Content-Range"] = "bytes " + startIndex + "-" + (startIndex + responseLength - 1) + "/" + fileLength; } response.Headers["Accept-Ranges"] = "bytes"; response.Headers["Content-Length"] = responseLength.ToString(); response.Cache.SetCacheability(HttpCacheability.Public); //required for etag output response.Cache.SetETag(etag); //required for IE9 resumable downloads response.ContentType = blob.Properties.ContentType; blob.DownloadRangeToStream(response.OutputStream, startIndex, responseLength); } }
Example:
Response.AddHeader("Content-Disposition", "attachment; filename=" + filename); // force download return new AzureBlobStream(blobContainerName, filename);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With