Binary response in ASP MVC
Today I wanted to give access to certain files on a website only via my DownloadController. This was so that I could ensure the current user had purchased the item in question first, and also sign any license info into the download aswell.
I tried getting a URL like this to work
http://localhost/download/1/SomeFileName
which would remap to the DownloadController
public void Index(int id, string fileName)
This worked fine, and because the URL ended with "SomeFileName" it would get saved as the correct filename too, but this was no use because SomeFileName has no file extension. As soon as I added .zip on the end the request no longer went via the new HttpHandler in the MVC web extensions. Even when I added it in the <httpHandlers> section of web.config it just wouldn’t work.
My problem was in relying on the url for the filename. This is apprarently not the way it should be done. Instead I should have stuck to the standard URL approach
http://localhost/download/1
and added a special HTTP header known as "content-disposition" to the response, this tells the client what the filename should be. Here is a full example of how to write a binary file to the Response when using the new MVC ASP Web Extensions, and how to have it saved on the client with the correct filename.
Thanks go to Phil Haak who pointed me in the right direction and was kind enough to promptly help a complete stranger!
I tried getting a URL like this to work
http://localhost/download/1/SomeFileName
which would remap to the DownloadController
public void Index(int id, string fileName)
This worked fine, and because the URL ended with "SomeFileName" it would get saved as the correct filename too, but this was no use because SomeFileName has no file extension. As soon as I added .zip on the end the request no longer went via the new HttpHandler in the MVC web extensions. Even when I added it in the <httpHandlers> section of web.config it just wouldn’t work.
My problem was in relying on the url for the filename. This is apprarently not the way it should be done. Instead I should have stuck to the standard URL approach
http://localhost/download/1
and added a special HTTP header known as "content-disposition" to the response, this tells the client what the filename should be. Here is a full example of how to write a binary file to the Response when using the new MVC ASP Web Extensions, and how to have it saved on the client with the correct filename.
public void Index(int id)
{
IProductRepository productRepository = EcoSpace.GetEcoService<IProductRepository>();
Product product = productRepository.GetByID(id);
if (product == null)
{
ViewData[GlobalViewDataKeys.ErrorMessage] = "Item not found";
Response.Redirect("/Account/Home", false);
return;
}
Response.ContentType = "Application/" + Path.GetExtension(product.DownloadUrl).Substring(1);
Response.AppendHeader("content-disposition", "inline; filename=" + product.DownloadUrl);
string localFileName = "";
if (product is Edition)
localFileName = FilePathUrls.Software;
else
if (product is Collateral)
localFileName = FilePathUrls.Collateral;
else
throw new NotImplementedException(product.GetType().Name);
localFileName = Request.MapPath(localFileName);
localFileName = Path.Combine(localFileName, product.DownloadUrl);
FileStream fileStream = new FileStream(localFileName, FileMode.Open, FileAccess.Read, FileShare.Read);
byte[] data = new byte[fileStream.Length];
using (fileStream)
fileStream.Read(data, 0, (int)fileStream.Length);
Response.BinaryWrite(data);
Response.End();
}
Thanks go to Phil Haak who pointed me in the right direction and was kind enough to promptly help a complete stranger!
Comments
A quick note: you might think about using Response.WriteFile instead of reading the whole file into memory and pushing it to output stream.
See this post
http://mrpmorris.blogspot.com/2008/02/custom-config-sections.html
Pete
direct response