Implementing a cross-browser solution for downloading files from a UNC share in ASP.NET applications
Table of contents
The scenario
I was working recently in an intranet application that had a download page. The output HTML was similar to the following:
<li><a class="download" href="\\MYSERVER\reports 2011\report1.zip" title="Report 1">Report 1</a></li> <li><a class="download" href="\\MYSERVER\reports 2011\report2.zip" title="Report 2">Report 2</a></li> <li><a class="download" href="\\MYSERVER\reports 2011\report 3.zip" title="Report 3">Report 3</a></li> <li><a class="download" href="\\MYSERVER\reports 2011\report 4.zip" title="Report 4">Report 4</a></li> <li><a class="download" href="\\MYSERVER\reports 2011\report 5&6.zip" title="Report 5&6">Report 5&6</a></li>
This was working fine in IE9, but not in other browsers. There was no action using Google Chrome, and using Firefox there was an error (HTTP Error 400 – Bad Request).
I tried to convert the file path to a file URI but it didn’t fix it. It continued to work on IE only.
<li><a class="download" href="file://MYSERVER/reports 2011/report1.zip" title="Report 1">Report 1</a></li> <li><a class="download" href="file://MYSERVER/reports 2011/report2.zip" title="Report 2">Report 2</a></li> <li><a class="download" href="file://MYSERVER/reports 2011/report 3.zip" title="Report 3">Report 3</a></li> <li><a class="download" href="file://MYSERVER/reports 2011/report 4.zip" title="Report 4">Report 4</a></li> <li><a class="download" href="file://MYSERVER/reports 2011/report 5&6.zip" title="Report 5&6">Report 5&6</a></li>
The solution was to create a custom ASP.NET download page. I used also jquery on the client side.
Step 1: Using jquery on the client side
The first step was to add an event handler to the download links. The request URI is encoded and is sent as a parameter to the download page. Creating an hidden iframe and setting the src attribute with the download link allows the file to be downloaded asynchronously.
$("a.download").bind("click", function (e) { e.preventDefault(); var requestedFile = encodeURIComponent($(this).attr('href')); var iframe = document.createElement("iframe"); iframe.src = 'Download.aspx?file=' + requestedFile; iframe.style.display = "none"; document.body.appendChild(iframe); // triggers download page });
Make sure you use encodeURIComponent function to encode special characters in the filename.
Step 2: Create an ASP.NET download page
This is the source code of the download page:
protected void Page_Load(object sender, EventArgs e) { try { string requestFile = Request.QueryString["file"]; if(string.IsNullOrEmpty(requestFile)) { throw new FileNotFoundException("File to download cannot be null or empty"); } // Get file name from URI string in C# // http://stackoverflow.com/a/1105614 var uri = new Uri(requestFile); string filename = Path.GetFullPath(uri.LocalPath); var fileInfo = new FileInfo(filename); if(!fileInfo.Exists) { throw new FileNotFoundException("File to download was not found", filename); } // get content type based on file extension. Example: // http://stackoverflow.com/a/691599 Response.ContentType = GetContentType(fileInfo.Extension); Response.AddHeader("Content-Disposition", "attachment; filename=\"" + fileInfo.Name + "\""); Response.WriteFile(fileInfo.FullName); Response.End(); } catch(ThreadAbortException) { // ignore exception } catch(FileNotFoundException ex) { Response.StatusCode = (int) System.Net.HttpStatusCode.NotFound; Response.StatusDescription = ex.Message; } catch(Exception ex) { Response.StatusCode = (int) System.Net.HttpStatusCode.InternalServerError; Response.StatusDescription = string.Format("Error downloading file: {0}", ex.Message); } }
Some notes:
This is necessary in order to make the download work with a UNC share (\\MYSERVER\….) or a file URI (file://….)
var uri = new Uri(requestFile); string filename = Path.GetFullPath(uri.LocalPath);
To avoid filename truncating, it’s necessary to wrap the filename with quotes
Response.AddHeader("Content-Disposition", "attachment; filename=\"" + fileInfo.Name + "\"");