How to Download Files Containing Special or Non-ASCII Characters in the Filename Using ASP.NET

Credit: http://lifehacker.com/5690522/how-to-enable-non+ascii-characters-on-linuxA couple months ago, I worked with file downloads that had filenames with non-ASCII Unicode characters, as well as special characters, such as semi-colons, commas, periods, and others (@, %, +, {, }, [, ], $, #, &, =). Here is an example of a filename that has all of those characters:  日本人 中國的 ~=[]()%+{}@;’#!$_&-  éè  ;∞¥₤€   .txt    

After a couple days of online research,  I finally found a solution that worked across different major browsers like IE8, IE9 and IE10, Firefox 21, Chrome 27, Safari 5 (on Windows). These are the most up-to-date versions as of this writing. Opera mostly works fine as well; however, I noticed that it doesn’t like the space and curly bracket characters.

Without going into detail about character encodings in the HTTP headers, I’d like to mention two major pieces of information you should know that I found regarding this issue:

1. According to the section 2.3 in RFC-2183 (Content-Disposition header field), the “filename” parameter in the Content-Disposition header can only use the US-ASCII characters.

2. However, as specified by RFC-5987 and RFC-2231, the correct encoding can be included right in the header field by using the filename* syntax and then by percent-encoding the non-ASCII characters in the filename. For example, for the filename “test-€.txt,” the content-disposition header would look like the following:  attachment; filename*=utf-8”test-%E2%82%AC.txt. Here is the .NET code that I had for writing the response header:

Response.AddHeader(“Content-Disposition”, “attachment; filename*=utf-8”” + Server.UrlEncode(fileName);

However, when I tried that syntax with my test filename  “日本人 中國的 ~=[]()%+{}@;’#!$_&-  éè  ;∞¥₤€   .txt,” I saw that all the spaces were replaced with “+” signs in Firefox, Chrome, IE9 and IE10 (“日本人+中國的+~=[]()%+{}@;’#!$_&-++éè++;∞¥₤€+++.txt”)

In Opera, it was worse. In addition to the “+” signs, the curly brackets were removed. In IE8 and Safari, the filename didn’t even appear. In the download dialog, it displayed the name of the ASP.NET page instead (Default.aspx in my case).

After that, I tried using “Uri.EscapeDataString(fileName)” instead of Server.UrlEncode() in the code above. The results were a little bit better but still varied depending on the browser.  In Firefox, IE10 and IE9, the filename showed correctly. However, IE8 and Safari still showed the ASP.NET page and now Chrome did too. As for Opera, it was still missing the curly brackets.

After further testing, I finally came up with the following solution that worked for all popular browsers that I tested with:

var headerValue = (Request.UserAgent.ToLower().Contains(“msie”))

? string.Format(“attachment; filename=\”{0}\””, Uri.EscapeDataString(fileName))

: string.Format(“attachment; filename=\”{0}\””, fileName); //for Firefox, Chrome, Safari, Opera

Response.Clear();

Response.ContentType = “text/plain”;

Response.AddHeader(“Content-Disposition”, headerValue);

Response.TransmitFile(filePath);

Response.End();

The trick was to set the content-encoding header based on the user-agent. For IE, I percent-encoded the filename using the Uri.EscapeDataString() method.  For the other browsers, I just left the filename string as is. It turned out the other browsers (at least the most recent versions as of this writing) didn’t need to use the RFC-5987 filename* syntax.

Related posts: