On demand asynchronous web image streaming for an IIS http handler class. A sessionless reusable solution for parallel binary data streaming on Internet.

One of the problems of dealing with html web page requests is that they are mostly processed in a top down fashion. If there are many image references, these will tend to load one at a time. Systems that do allow multiple requests can easily be put on hold by the receiving seerver is session state is enabled and maximum connections per session is in place.
How to bypass these mechanisms to ensure direct fast delivery of information via http... Use sessionless http handlers in a reusable mode.

HTTP Handler Class

In order to achieve what we want, a seperate class is created, not reliant on the asp.net 'ashx' handeler. The code below is an example of such a service. It is used to respond to requests for thumbnails of PDF files. If the thumbnail images for the requested resource are available it streams them directly via the re response else it will try to locate the PDF file and request a PDF to bitmap rendering routine to generate it and upon receipt stream it out.

Imports System

Imports System.IO

Imports System.Web

 

Public Class FileStreamer

    Implements System.Web.Routing.IRouteHandler

    Function GetHttpHandler(requestContext As Routing.RequestContext) As IHttpHandler Implements System.Web.Routing.IRouteHandler.GetHttpHandler

        Dim MyhttpHandler As HttpHandler = New HttpHandler()

        Return MyhttpHandler

    End Function

 

    Public Class HttpHandler

        Implements IHttpHandler

        Public ReadOnly Property IsReusable As Boolean Implements System.Web.IHttpHandler.IsReusable

            Get

                Return True

            End Get

        End Property

 

        Public Sub ProcessRequest(context As System.Web.HttpContext) Implements System.Web.IHttpHandler.ProcessRequest

            System.Net.ServicePointManager.DefaultConnectionLimit = 65535

            Dim ImageFileRef As String = ""

            Dim Size As Integer = 500

            Dim Transparency As Integer = 0

            If Not IsNothing(context.Request.QueryString("img")) Then

                ImageFileRef = context.Request.QueryString("img")

            End If

            If Not IsNothing(context.Request.QueryString("s")) Then

                If IsNumeric(context.Request.QueryString("s")) Then

                    If context.Request.QueryString("s") < 2001 And context.Request.QueryString("s") > 10 Then

                        Size = context.Request.QueryString("s")

                    End If

                End If

            End If

            If Not IsNothing(context.Request.QueryString("t")) Then

                If IsNumeric(context.Request.QueryString("t")) Then

                    If context.Request.QueryString("t") = 1 Then

                        Transparency = 1

                    End If

                End If

            End If

            '// Image Locator and streamer

            Try

                If ImageFileRef.Length > 0 Then

                    ImageFileRef = ImageFileRef.Replace(".pdf", ".png")

                    If File.Exists(HttpContext.Current.Server.MapPath("\Uploads\" & ImageFileRef)) Then

                        Dim Myinput As System.IO.FileStream = New System.IO.FileStream(HttpContext.Current.Server.MapPath("\Uploads\" & ImageFileRef), FileMode.Open, FileAccess.Read)

                        Dim Buffer() As Byte

                        ReDim Buffer(Myinput.Length) ' - 1)

                        Myinput.Read(Buffer, 0, Buffer.Length)

                        Myinput.Close()

                        Myinput.Dispose()

                        context.Response.Clear()

                        context.Response.ContentType = "image/png"

                        context.Response.AddHeader("Content-Type", "image/png")

                        context.Response.AddHeader("Content-Length", Buffer.Length.ToString)

                        context.Response.AddHeader("Content-Transfer-Encoding", "binary")

                        context.Response.BinaryWrite(Buffer)

                        context.Response.Cache.SetCacheability(HttpCacheability.NoCache)

                        context.Response.Cache.SetAllowResponseInBrowserHistory(False)

                        context.Response.Flush()

                        context.Response.Close()

                        GC.Collect()

                    Else

                        '// See if we can generate the preview

                        Dim Sourcefile As String = ImageFileRef

                        '// transform to PDF & page

                        Dim NewSource As String = Sourcefile.Replace(".png", ".pdf")

                        Dim Path As String = HttpContext.Current.Server.MapPath("\PDFUploads")

                        If ImageCoder.GeneratePDFThumbnail(Path, NewSource, Size, Transparency, 0) Then

                            Dim Checkfile As String = ImageFileRef

                            Checkfile = Checkfile.Replace(".pdf", ".png")

                            If File.Exists(HttpContext.Current.Server.MapPath("\PDFUploads\" & ImageFileRef)) Then

                                Dim Myinput As System.IO.FileStream = New System.IO.FileStream(HttpContext.Current.Server.MapPath("\PDFUploads\" & ImageFileRef), FileMode.Open, FileAccess.Read)

                                Dim Buffer() As Byte

                                ReDim Buffer(Myinput.Length) ' - 1)

                                Myinput.Read(Buffer, 0, Buffer.Length)

                                Myinput.Close()

                                Myinput.Dispose()

                                context.Response.Clear()

                                context.Response.ContentType = "image/png"

                                context.Response.AddHeader("Content-Type", "image/png")

                                context.Response.AddHeader("Content-Length", Buffer.Length.ToString)

                                context.Response.AddHeader("Content-Transfer-Encoding", "binary")

                                context.Response.BinaryWrite(Buffer)

                                context.Response.Cache.SetCacheability(HttpCacheability.NoCache)

                                context.Response.Cache.SetAllowResponseInBrowserHistory(False)

                                context.Response.Flush()

                                context.Response.Close()

                                GC.Collect()

                            Else

                                NoImage(context)

                            End If

                        Else

                            NoImage(context)

                        End If

                    End If

               Else

                    context.Response.Flush()

                    context.Response.Close()

                    GC.Collect()

                End If

            Catch ex As Exception

                Errorhandler.ErrorHandler(0, ex.ToString)

                context.Response.Flush()

                context.Response.Close()

                GC.Collect()

            End Try

        End Sub

 

        Sub NoImage(context As System.Web.HttpContext)

            '// Stream no image

            Dim Myinput As System.IO.FileStream = New System.IO.FileStream(ConfigurationManager.AppSettings("NoImage"), FileMode.Open, FileAccess.Read)

            Dim Buffer() As Byte

            ReDim Buffer(Myinput.Length) ' - 1)

            Myinput.Read(Buffer, 0, Buffer.Length)

            Myinput.Close()

            Myinput.Dispose()

            context.Response.Clear()

            context.Response.ContentType = "image/png"

            context.Response.AddHeader("Content-Type", "image/png")

            context.Response.AddHeader("Content-Length", Buffer.Length.ToString)

            context.Response.AddHeader("Content-Transfer-Encoding", "binary")

            context.Response.BinaryWrite(Buffer)

            context.Response.Cache.SetCacheability(HttpCacheability.NoCache)

            context.Response.Cache.SetAllowResponseInBrowserHistory(False)

            context.Response.Flush()

            context.Response.Close()

            GC.Collect()

        End Sub

    End Class

End Class

Dealing with reusable sessionless http handlers

Points to take into account when working with http handlers

Create a new class (FileStreamer) and add Implements System.Web.Routing.IRouteHandler, add the GetHttpHandler making sure it implements System.Web.Routing.IRouteHandler.GetHttpHandler as shown above.

NOTE: Set 'IsReusable' to true. Something to watch for is that it would appear this is not thread safe so you need to cover all the bases and do the garbage collection yourself! The advantage is that instead of it being created and then disposed for each and every call, it will be loaded into memory on the initial call and is then reused on demand giving that extra bit of performance.

Wiring the image streamer class to the Internet

We need to connect our new image streaming class directly to the Internet to recieve requests. For this we need to inform the IIS website that we want this code to deal with requests directly. We do this by creating a route mapping table and activating the IIS route handler in the Global.asax file as follows:

  • Add namespace System.Web.Routing
  • Add routes to ignore (all the normal resources on offer)
  • Finall Attach the stremer class: 'routes.Add(New Route("FileStream", New FileStreamer()))' (The Magic: directly wired up! and fires up the image streaming class)

<%@ Application Language="VB" %>

<%@ Import Namespace="System.Threading" %>

<%@ Import Namespace="System.Web.Routing" %>

 

<script runat="server">

 

    Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)

        ' Code that runs on application startup

        RouteTable.Routes.Ignore("{resource}.axd")

        RouteTable.Routes.Ignore("{resource}.ashx")

        RouteTable.Routes.Ignore("{resource}.js")

        RouteTable.Routes.Ignore("{resource}.css")

        RouteTable.Routes.Ignore("{resource}.png")

        RouteTable.Routes.Ignore("{resource}.jpg")

        RouteTable.Routes.Ignore("{resource}.eps")

        RouteTable.Routes.Ignore("{resource}.pdf")

        RouteTable.Routes.Ignore("{resource}.htm")

        RouteTable.Routes.Ignore("{resource}.aspx")

        RegisterRoutes(RouteTable.Routes)

    End Sub

 

    Sub Application_End(ByVal sender As Object, ByVal e As EventArgs)

        ' Code that runs on application shutdown

    End Sub

 

    Sub Application_Error(ByVal sender As Object, ByVal e As EventArgs)

        ' Code that runs when an unhandled error occurs

    End Sub

 

 

    Sub Session_Start(ByVal sender As Object, ByVal e As EventArgs)

        ' Code that runs when a new session is started

    End Sub

 

    Sub Session_End(ByVal sender As Object, ByVal e As EventArgs)

        ' Code that runs when a session ends.

    End Sub

 

    Sub Application_BeginRequest(ByVal sender As Object, ByVal e As EventArgs)

        '// snip snip....

    End Sub

 

    Sub RegisterRoutes(ByVal routes As RouteCollection)

        routes.Add(New Route("FileStream", New FileStreamer()))

    End Sub

</script>

 

Implementing the image streamer class for use in web pages

Where normall you would have a URL link directly to a resource... like 'href="http://T.L.D/path/resource.ref"', you now sent it to the streamer 'href="http://T.L.D/FileStream?ref=resource"'. Once the URL has been identified and a request sent, the following requests to the 'same' URL can follow in fast progression, all requests are handled and processed. Available files are streamed immediately, those that need to be created are streamed as they become available.

Asynchronous image streamer results

Asyncronour Image Streaming Results

Before the Flash application that uses the image resources has even finished loading, the image requestes are dealt with and delivered in a parallel asynchronous way.

Live Demo - parallel asynchronous image generation and streaming

It's async, fast and delivers on demand without holding up the show.

A live streaming demo that allows you to upload PDF files (regardless of content complexity or amount of pages). It will generate a thumbnail preview of each PDF page and stream the bitmap images as soon as these become available. This method of delivering content on demand as it becomes available was developed to support and suppliment another project that generates bitmap images from PDF files.