response.TransmitFile bad coding day

File Downloads from ASP.NET

All I had to do was serve the file as a download page to the customer, there the pain began.

If providing file downloads, it is important to buffer stream the file to the user to manage the server memory. If many users start downloading your files together and you are not buffering the file, then the whole will be held in memory multiple times thus causing memory exhaustion. Buffering means only a few bytes of each downloading file is held in memory at once.

response.TransmitFile

In .NET 2.0 a great new method was added to the framework, “response.TransmitFile”. This method takes out a load of code that we used to have to code whenever we wanted to provide a file to the customer for download. It streams the file to the user and handles the buffering of the file. Look at the following simple code.

response.Clear()
response.ClearContent()
response.ClearHeaders()
response.ContentType = "application/x-msexcel"
response.AppendHeader("Content-Disposition", "attachment; filename=PriceList.xlsx")
response.TransmitFile(StrFilename)
response.End()

Now compare that to this version where no buffering is ocurring, the whole file is read to memory then sent to browser Protect files for download - Code Project


    'This is the function that you have to use
    Private Function downloadfile(ByVal strFile As String)
        Dim fs As FileStream
        Dim strContentType As String
        ' This is the important part, because you going to use the local path 
        'to get the file
        Dim strPath = Me.Server.MapPath( _
            System.Configuration.ConfigurationSettings.AppSettings("uploadDirectory")) & "\"

        Dim strFileName As String = strFile
        fs = File.Open(strPath & strFileName, FileMode.Open)
        Dim bytBytes(fs.Length) As Byte
        fs.Read(bytBytes, 0, fs.Length)
        fs.Close()
        Response.AddHeader("Content-disposition","attachment; filename=" & strFileName)

        Response.ContentType = "application/octet-stream"
        Response.BinaryWrite(bytBytes)
        Response.End()

        Return True
    End Function

The problem

I got the error below when I implemented my download page;

The file you are trying to open, xxxxxx is in a different format 
than specified by the file extension. Verify that the file is not corrupted 
and is from a trusted source before opening the file. 
Do you want to open the file now?

I could not open the excel file successfully. I opened the downloaded file in textpad to look at what was happening inside, but as office stores its files in zips this didn’t reveal anything immediately to my eye. Instead I tried using a text file to download and that revealed the issue that lead to me finding my mistake.

The downloaded file exhibited corruption, and thus I started looking for issues that could corrupt the file. These included tracing being switched on for the site, IIS settings, wondering if it has something to do with my new Windows 7 machine etc. After several hours from starting what should have been a twenty minute job, I discovered my error. See my erroneous below;

My error

    ' WARNING THIS CODE SMIPPET HAS AN ERROR DO NOT COPY!
    Dim StrFilename = Files(0) 
    response.Clear()
    response.ContentType = "application/x-msexcel"
    response.AppendHeader("Content-Disposition", "attachment; filename=PriceList.xlsx") 
    response.TransmitFile(StrFilename, IO.FileMode.Open, IO.FileAccess.Read)
    response.End()

See what I’d done – I didn’t for ages. As I had taken some code from another streaming download that I wrote in .NET 1.1 I copied the filename and file opening line. Only I also copied the io.FileMode.Open, IO.FileAccess.Read, as it happens there is an overload for TransmitFile that takes and offset in the file and length. Of course the enumeration for IO.FileMode.Open resolves to an integer so no compile error or runtime error, instead my file was truncated to virtually nothing!

On getting rid of these two parameters in all worked great! A number of hours wasted eliminating issues down to the line of code that were wrong. Funny how you can create masses of functionality and code one day and another get bogged down by a stupid error like this!