Ok. So this wasn’t a bug in either IIS or ASP.NET…it was a bug in my code.
The ASP.NET page reads a PDF file from the disk into a byte array (byte[]) and then squirts it out through the HttpResponse using BinaryWrite(). Here’s the code…
private void WriteRequestedDocumentToResponse(string filename)
{
Response.AddHeader(
“Content-Disposition”, “attachment;filename=” + filename);
Response.BinaryWrite(GetFileAsByteArray(pathToFile));
}
This code has been in production and working for a few weeks and then yesterday the client posted a new version of the PDF file and all of a sudden Acrobat started saying “There was an error opening this document. The file is damaged and could not be repaired.“ My first instinct was to say “well, the pdf’s messed up“ but when the file got downloaded and saved to my local disk, it kept coming out about 1kb bigger than it was supposed to be.
My second thought was to suspect the GetFileAsByteArray() method even though that method had been used in a bunch of different projects without incident.
private byte[] GetFileAsByteArray(string fileName)
{
FileStream fs;
fs=File.Open(fileName, FileMode.Open, FileAccess.Read);
byte[] buffer=new byte[fs.Length];
fs.Read(buffer, 0, System.Convert.ToInt32(fs.Length));
fs.Close();
return buffer;
}
So, looking at the GetFileAsByteArray() method, had a moment of panic looking at the Convert.ToInt32 line. I’m like, “oh…dammit! fs.Length is a ‘long’ and fs.Read() is changing that value to an int. Crap! It’s trimming a bunch of stuff because the int value is maxxing out. After checking what the max value of an Int32 is and running some unit tests, well, let’s just say that that turned out to be utter foolishness on my part. This code will support files bigger than 2GB. Well, this method checks out.
So, what about those extra bytes that sometimes get tagged onto the downloaded PDFs? I opened up the non-corrupted raw PDF data using Visual Studio and scrolled to the end.
Here are the last 10 lines of the good file.
0000220704 00000 n
0000220739 00000 n
0000220763 00000 n
0000220848 00000 n
0000224467 00000 n
trailer
<>
startxref
116
%%EOF
Looks pretty normal and the PDF standard is even nice enough to put an explicit EOF into the file.
And now the corrupted file…
AH! Now, is it totally obvious what’s wrong? After I write the file out to the HttpResponse, the ASP.NET page continues to be rendered. BAH!
So, the solution is to add a call to Response.End() right after the BinaryWrite() and stop further processing of the ASP.NET page. Duh! And I added a Response.Clear() just to make double sure that the response is clean before I start writing the file.
private void WriteRequestedDocumentToResponse(string filename)
{
Response.Clear();
Response.AddHeader(
“Content-Disposition”, “attachment;filename=” + filename);
Response.BinaryWrite(GetFileAsByteArray(pathToFile));
Response.End();
}
So that’s that.
-Ben
Leave a Reply