1

I have an annoying problem: I should show a PDF in the browser (inline-display, not download).

So far, with the code below, it works for internet explorer. But in google-chrome, it just downloads.

On the same server, a 3rd party application that does the same works fine.
I suppose the problem is the "appliction/octet-stream" that you see in the content-type header...

I find this rather annoying.
My code sets content-type application/pdf, and when I look at the actual headers sent, i see it is application/octet-stream...

According to https://superuser.com/questions/219870/how-to-open-pdf-in-chromes-integrated-viewer-without-downloading-it#

this is because the mime is octet-stream instead of application/pdf...

And I have just one question: Why ? Why ? Why ? (Why does it set octet-stream, not application/pdf as set in the code - See full code below)
Bonus question: Why is Transfer-Encoding chunked if i set Content-Length to the length of the byte-array ?

The funny thing is, it works fine on my local development server, so this seems to have something to do with the evils of IIS >= 7...

Bad HTTP Headers

ashx:

    Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
        Dim baPDF As Byte() = GetPdfFromImage(Me.Data)
        'context.Response.Write(COR.Tools.JSON.JsonHelper.Serialize(Me.Data(context)))

        context.Response.Clear()
        'context.Response.AddHeader("Content-Disposition", "attachment; filename=" + strFileName)
        context.Response.AddHeader("Content-Disposition", Portal.ASP.NET.GetContentDisposition("Drucken.pdf", "inline"))
        context.Response.AddHeader("Content-Length", baPDF.Length.ToString())
        ' context.Response.ContentType = "application/msword"
        ' context.Response.ContentType = "application/octet-stream"

        ' https://superuser.com/questions/219870/how-to-open-pdf-in-chromes-integrated-viewer-without-downloading-it#
        ' context.Response.ContentType = "text/html"
        context.Response.ContentType = "application/pdf"



        context.Response.BinaryWrite(baPDF)
        context.Response.Flush()

        context.Response.End()
    End Sub





    ' COR.ASP.NET.StripInvalidPathChars("") '
    Public Shared Function StripInvalidPathChars(str As String) As String
        Dim strReturnValue As String = Nothing

        If str Is Nothing Then
            Return strReturnValue
        End If

        Dim sb As System.Text.StringBuilder = New System.Text.StringBuilder()
        Dim achrInvalidPathChars As Char() = System.IO.Path.GetInvalidPathChars()


        For Each cThisChar As Char In str
            Dim bIsValid As Boolean = True

            For Each cInvalid As Char In achrInvalidPathChars
                If cThisChar = cInvalid Then
                    bIsValid = False
                    Exit For
                End If
            Next cInvalid

            If bIsValid Then
                sb.Append(cThisChar)
            End If
        Next cThisChar

        strReturnValue = sb.ToString()
        sb = Nothing
        Return strReturnValue
    End Function ' StripInvalidPathChars '


    Public Shared Function GetContentDisposition(ByVal strFileName As String) As String
        Return GetContentDisposition(strFileName, "attachment")
    End Function ' GetContentDisposition '


    ' http://www.iana.org/assignments/cont-disp/cont-disp.xhtml '
    Public Shared Function GetContentDisposition(ByVal strFileName As String, ByVal strDisposition As String) As String
        ' http://stackoverflow.com/questions/93551/how-to-encode-the-filename-parameter-of-content-disposition-header-in-http '
        Dim contentDisposition As String
        strFileName = StripInvalidPathChars(strFileName)

        If String.IsNullOrEmpty(strDisposition) Then
            strDisposition = "inline"
        End If

        If System.Web.HttpContext.Current IsNot Nothing AndAlso System.Web.HttpContext.Current.Request.Browser IsNot Nothing Then
            If (System.Web.HttpContext.Current.Request.Browser.Browser = "IE" And (System.Web.HttpContext.Current.Request.Browser.Version = "7.0" Or System.Web.HttpContext.Current.Request.Browser.Version = "8.0")) Then
                contentDisposition = strDisposition + "; filename=" + Uri.EscapeDataString(strFileName).Replace("'", Uri.HexEscape("'"c))
            ElseIf (System.Web.HttpContext.Current.Request.Browser.Browser = "Safari") Then
                contentDisposition = strDisposition + "; filename=" + strFileName
            Else
                contentDisposition = strDisposition + "; filename*=UTF-8''" + Uri.EscapeDataString(strFileName)
            End If
        Else
            contentDisposition = strDisposition + "; filename*=UTF-8''" + Uri.EscapeDataString(strFileName)
        End If

        Return contentDisposition
    End Function ' GetContentDisposition '

This is the header of the 3rd party application, where Chrome displays it fine Aperture Headers

Community
  • 1
  • 1
Stefan Steiger
  • 78,642
  • 66
  • 377
  • 442

2 Answers2

1

The solution is as simple as it is hideous.

Thanks to a coworker change, the code was in an ashx file directly, not in ashx.vb.

The good part of this is, one can modify the ashx on the server, even when you have a compiled web-application. The bad part is, this has the same effect of compile on the fly as a website project.

Consequently, when you just recompile the application and put the MyWebApplication.dll onto the server, this leaves the old ashx as is.

And since ASP.NET uses the code in the ashx rather than the compiled dll, it always uses the old code, as long as you don't update the ashx file, too.

Changed it now, and it began working immediately.
Now, that was a good one...
There never was a mistake in the code to begin with.

Stefan Steiger
  • 78,642
  • 66
  • 377
  • 442
  • My VS2008 won't open an .ashx file by default, it opens the .ashx.vb file even when you double-click the .ashx file in Solution Explorer. It must have taken some doing to break it that way(!) I assume your coworker is now enthusiastic about using code-behind. – Andrew Morton Feb 26 '14 at 09:32
  • @Andrew Morton: Right-click, and select "edit markup" or whatever it is called. The other way is to copy the text into a notepad window, and delete the .vb file. – Stefan Steiger Feb 26 '14 at 19:18
  • Aw no, don't go letting everyone know how to get to it, or they'll all be using "V*ew M*rk*p". I am embarassingly aware of many ways to mess up my website code, I just hadn't got to that way myself yet. ;) – Andrew Morton Feb 26 '14 at 19:26
  • @Andrew Morton: On that subject, another good way to hell is to zip your app, copy it on the server, unzip it with windows, and copy it to the destination directory. On certain systems, you'll be missing the *.js files afterwards... – Stefan Steiger Oct 24 '14 at 15:01
0

You need to clear the headers before setting the header you want.

From Microsoft's page about Response.Clear.

The Clear method does not clear header information.

Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
    Dim baPDF As Byte() = GetPdfFromImage(Me.Data)

    context.Response.ClearHeaders()
    context.Response.ContentType = "application/pdf"

    ... ' Cut for brevity
End Sub
Adam Zuckerman
  • 1,633
  • 1
  • 14
  • 20