views:

104

answers:

2

I am using .NET 3.5 ASP.NET. Currently my web site serves a PDF file in the following manner:

context.Response.WriteFile(@"c:\blah\blah.pdf");

This works great. However, I'd like to serve it via the context.Response.Write(char [], int, int) method.

So I tried sending out the file via

byte [] byteContent = File.ReadAllBytes(ReportPath);
ASCIIEncoding encoding = new ASCIIEncoding();
char[] charContent = encoding.GetChars(byteContent);
context.Response.Write(charContent, 0, charContent.Length);

That did not work (e.g. browser's PDF plugin complains that the file is corrupted).

So I tried the Unicode approach:

byte [] byteContent = File.ReadAllBytes(ReportPath);
UnicodeEncoding encoding = new UnicodeEncoding();
char[] charContent = encoding.GetChars(byteContent);
context.Response.Write(charContent, 0, charContent.Length);

which also did not work.

What am I missing?

+4  A: 

You should not convert the bytes into characters, that is why it becomes "corrupted". Even though ASCII characters are stored in bytes the actual ASCII character set is limited to 7 bits. Thus, converting a byte stream with the ASCIIEncoding will effectively remove the 8th bit from each byte.

The bytes should be written to the OutputStream stream of the Response instance.

Instead of loading all bytes from the file upfront, which could possibly consume a lot of memory, reading the file in chunks from a stream is a better approach. Here's a sample of how to read from one stream and then write to another:

void LoadStreamToStream(Stream inputStream, Stream outputStream)
{
    const int bufferSize = 64 * 1024;
    var buffer = new byte[bufferSize];

    while (true)
    {
        var bytesRead = inputStream.Read(buffer, 0, bufferSize);
        if (bytesRead > 0)
        {
            outputStream.Write(buffer, 0, bytesRead);
        }
        if ((bytesRead == 0) || (bytesRead < bufferSize))
            break;
    }
}

You can then use this method to load the contents of your file directly to the Response.OutputStream

LoadStreamToStream(fileStream, Response.OutputStream);

Better still, here's a method opening a file and loading its contents to a stream:

void LoadFileToStream(string inputFile, Stream outputStream)
{
    using (var streamInput = new FileStream(inputFile, FileMode.Open, FileAccess.Read))
    {
        LoadStreamToStream(streamInput, outputStream);
        streamInput.Close();
    }
}
Peter Lillevold
Thanks. This worked great. Can you elaborate on 2 things? Can I delete the file after calling LoadFileToStream, and then Response.Flush? And why does converting chars to bytes cause corruption. Are they not both 8 bits?
AngryHacker
You can absolutely delete the file after LFTS, if you no longer need it around that is.
Peter Lillevold
Elaborated on the encoding problem in my answer
Peter Lillevold
What encoding should then do the trick? UTF8?
AngryHacker
No encoding. You really don't want to convert the binary stream into some other format, it will make the file unreadable, as your experience with the PDF file shows.
Peter Lillevold
Understood. I didn't really grok what encoding did, since I never had to use it before. I get it now, thank you.
AngryHacker
+1  A: 

may also need to set Contenttype by doing something like this

Response.ContentType = "application/octet-stream";

TheOCD