Saturday, January 28, 2012

The correct way to download text files

Downloading a file - a common task you have to deal in many projects. Generally it is quite simple - all you have to do is to read a file or a string content into memory and dump it into HttpResponse.

public static void DownloadFile(string content, string fileName, bool addBOM)
{
     DownloadFile(System.Text.Encoding.UTF8.GetBytes(content), fileName, addBOM);
}

public static void DownloadFile(byte[] content, string fileName, bool addBOM)
{
     if (content != null)
     {
          byte[] bom = System.Text.Encoding.UTF8.GetPreamble();
          int contentLength = content.Length + (addBOM ? bom.Length : 0);

          HttpContext.Current.Response.ClearHeaders();
          HttpContext.Current.Response.Clear();
          HttpContext.Current.Response.AddHeader("Content-Disposition", "attachment; filename=" + fileName);
          HttpContext.Current.Response.AddHeader("Content-Length", contentLength.ToString());
          HttpContext.Current.Response.ContentType = "application/octet-stream";
          HttpContext.Current.Response.Flush();

          if (addBOM)
          {
               HttpContext.Current.Response.BinaryWrite(bom); 
          }
           
          HttpContext.Current.Response.BinaryWrite(content);
          HttpContext.Current.Response.End();
     }
}

The example shows how the UTF8 text file should be pushed for download. I believe this is most popular encoding for the text files containing localized data. Couple things to be noted here:
  • Some text editors behave differently while opening UTF8 encoded files with BOM (byte order mark) and without it. This is mostly related to legacy text editors which might handle BOM prefixed files incorrectly. So you might have a requirement to include BOM information into the file you are presenting for download or just skip it. 
  • Adding a content-length header is a very good practice. This provides the client browser with the information required to display download progress correctly. When you have a requirement to download a string content as a file, use the appropriate encoding when calculating content length value. Relying on the string content length is not always the good solution. When you have a UTF8 encoded string all the localized international characters are encoded using 2 bytes, therefor calculating string length in characters would actually cut 1 byte off any international character available in the string.

2 comments:

  1. A tip for Sharepoint developers. If it works only the first time after the page load and does not work after that, you might need to overwrite onsubmit handler of the form with something like this:

    Button.OnClientClick = "window.WebForm_OnSubmit = function() {return true;}";

    ReplyDelete
  2. More information about this Sharepoint related issue can be found here: http://social.msdn.microsoft.com/Forums/en-US/sharepointdevelopment/thread/107b2c17-07fe-4a15-ad81-dcb31e1e9c84/

    ReplyDelete