2

I have some images in a SQL Server database. I want to display them when a user clicks a button, but I want them to load into the browser as a file so that when the user resizes the window the images automatically resize.

For example in Firefox if you go to File->Open File... and select an image on your computer, it will load it into a new window and resize it for you when you drag the scrollbars. My problem is, how do I get the image to load from a SQL Server database into the browser as a file? I have the following code on the form where the user clicks view within a GridView:

<script type="text/javascript">
    function ShowImageInNewPage(url_add) {
        window.open(url_add, 'ViewScreenshot', 'resizable=yes,scrollbars = yes');
    }
</script>

<asp:GridView 
 ID="grdTrades" 
 runat="server" 

 <... removed some properties for brevity ...>
 >
 <Columns>
  <asp:CommandField ShowSelectButton="true" ButtonType="Link" SelectText="Select" /> 

  <.. removed some columns for brevity ...>

  <asp:TemplateField HeaderText="Screenshot" >
   <ItemTemplate>
    <input type="button" size="x-small" value="View" onclick="javascript:ShowImageInNewPage('DisplayImage.aspx?screenshotId=<%# Eval("screenshotId") %>');" />
   </ItemTemplate>
  </asp:TemplateField>
 </Columns>
</asp:GridView>

DisplayImage.aspx has this code:

<script runat="server">
    protected void Page_Load(object sender, EventArgs e)
    {        
        if (Request.QueryString["screenshotId"] != null)
        {            
            int screenshotId= int.Parse(Request.QueryString["screenshotId"]);
            imgScreenshot.ImageUrl = "App_Handlers/ImageHandler.ashx?screenshotId=" + screenshotId;            
        }
    }    
</script>

<html>
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:Image ID="imgScreenshot" runat="server" />
    </div>
    </form>
</body>
</html>

App_Handlers/ImageHandler.ashx has this code:

<%@ WebHandler Language="C#" Class="ImageHandler" %>

using System;
using System.Web;
using System.Data;
using DatabaseComponent;

public class ImageHandler : IHttpHandler {

    public void ProcessRequest (HttpContext context) {

        if (context.Request.QueryString["screenshotId"] != null)
        {
            int screenshotId = int.Parse(context.Request.QueryString["screenshotId"]);

            DBUtil DB = new DBUtil();
            DataTable dt = DB.GetScreenshot(screenshotId);

            if (dt != null)
            {
                Byte[] bytes = (Byte[])dt.Rows[0]["screenshot"];
                context.Response.Buffer = true;
                context.Response.Charset = "";
                context.Response.Cache.SetCacheability(HttpCacheability.NoCache);
                context.Response.ContentType = dt.Rows[0]["contentType"].ToString();
                context.Response.AddHeader("content-disposition", "attachment;filename=" + dt.Rows[0]["fileName"].ToString());
                context.Response.BinaryWrite(bytes);               
                context.Response.Flush();
                context.Response.End();
            }
        }        
    }

    public bool IsReusable {
        get {
            return false;
        }
    }
}

Here are the response headers for DisplayImage.aspx

Server: ASP.NET Development Server/10.0.0.0
Date: Wed, 10 Nov 2010 13:13:37 GMT
X-AspNet-Version: 4.0.30319
Transfer-Encoding: chunked
Cache-Control: no-cache
Pragma: no-cache
Expires: -1
Content-Type: image/JPG
Connection: Close

I've changed my original question to use an image handler now thanks @leppie. Thanks for looking!

UPDATE

I've now changed DisplayImage.aspx to this code, but now I get some real weird behavior. When I click on an image link, this page loads and firefox asks if I want to save DisplayImage.aspx. What is going on? How can I get Firefox to load the image as a file?

DisplayImage.aspx:

<%@ Page Language="C#" ContentType="image/*"  %>

<script runat="server">        
</script>

<html>
<head runat="server">
    <title></title>
</head>
<body>
    <img  src="App_Handlers/ImageHandler.ashx?screenshotId=<%=Request.QueryString["screenshotId"]%>" /> 
</body>
</html>

If I remove ContentType from the Page declaration the image loads into a new window however the image won't zoom, and won't resize itself. Pulling my hair out...

Mark Allison
  • 6,838
  • 33
  • 102
  • 151

4 Answers4

3

You should not use a page to return the image data. Instead you should use an HttpHandler. An HttpHandler is a class that implements the IHttpHandler interface. The IHttpHandler interface is more low-level than Page. Page does in fact implement IHttpHandler itself, but the purpose of Page is to return html, which is why you should not be using it for returning image data.

Klaus Byskov Pedersen
  • 117,245
  • 29
  • 183
  • 222
  • A page is just as good, and probably better seeing you do not need permissions to install handlers/modify the web.config. – leppie Nov 10 '10 at 09:37
  • @leppie, you don't need to edit web.config to install an httphandler. A .ashx file works just as well. – Klaus Byskov Pedersen Nov 10 '10 at 09:41
  • OK, I can rewrite using an ashx file, but back to the question. How do I load the image into the browser as a file, and not as an image on a page? – Mark Allison Nov 10 '10 at 09:53
  • @leppie - I've edited my question so that I'm now using a handler. Please could you address the issue of loading the image into the browser as a file? Thanks. – Mark Allison Nov 10 '10 at 10:24
  • To load the image as a file you need to return the image data in the response stream and set the mime type in the header to image/jpeg, or whatever format it is. The mime type is what tells the browser how to interpret the response. – James Gaunt Nov 10 '10 at 10:40
  • @James - I've edited my Question to reflect my new code. What changes do I need to make to DisplayImage.aspx as this is the page I am redirecting to when displaying the image. This page calls the HttpHandler to get the image, and in here I am setting the Mime Type. Can I remove the image control on DisplayImage.aspx? – Mark Allison Nov 10 '10 at 12:50
  • @James - I've also added the Http Reponse headers on DisplayImage.aspx. – Mark Allison Nov 10 '10 at 13:18
1

If you want to do this using a page (e.g. ImageLoad.aspx) then something like this in Page_Load should suffice:

protected void Page_Load(object sender, EventArgs e)
{
    HttpResponse response = HttpContext.Current.Response;

    response.Clear();
    response.ContentType = "image/png";

    Image outputImage = (Load your image here)
    MemoryStream ms = new MemoryStream();
    outputImage.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
    ms.WriteTo(response.OutputStream);
    outputImage.Dispose();
    ms.Close();

    response.End();
}

Alternatively with a handler:

public class ImageHandler : IHttpHandler
{
    public bool IsReusable
    {
        get { return true; }
    }

    public void ProcessRequest(HttpContext context)
    {
        try
        {
            HttpResponse response = context.Response;

            response.Clear();
            response.ContentType = "image/png";

            Image outputImage = (Load your image here)
            MemoryStream ms = new MemoryStream();
            outputImage.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
            ms.WriteTo(response.OutputStream);
            outputImage.Dispose();
            ms.Close();

            response.End();
        }
        catch
        {
            context.Response.End();
        }
    }
}

Just replace (Load your image here) with the relevant code based on how you specify the image (presumably as a request parameter).

As suggested in other answers set relevant caching headers on the request if you want to save some clock cycles.

James Gaunt
  • 14,631
  • 2
  • 39
  • 57
  • Thanks James, I've tried your code and it has the same effect as my code. Try this: with Firefox open an image from your local hard disk, now resize the window. The image gets resized automatically. I want my image to load from my database into the browser in the same way so the browser thinks its a file. The method you've just shown me, and my method shown in my question simply displays the image but when I resize the browser, the image does not get resized. Any ideas on how to fix this? – Mark Allison Nov 10 '10 at 15:02
  • @James - here's an example of how I want my images to load. Click on an image on this page and then resize your browser to see what I mean. http://www.phpbb.com/community/viewtopic.php?f=6&t=2109563 – Mark Allison Nov 11 '10 at 02:19
  • I can't see what is special about the way this image is delivered. I've tried opening a few images in Firefox and they all open like this. They shrink if the browser window is smaller, but don't expand if it's bigger. You can run Live HTTP Headers in Firefox and see exactly what headers are sent with the image - which I've done here and again - nothing unusual there jumps out. Maybe try comparing header for header with the image you are serving. – James Gaunt Nov 11 '10 at 20:09
  • @James, well if you open a file locally it doesn't have any headers because it's an image file, not a web page. I've changed my code as shown in the UPDATE section of my question. Any further ideas? – Mark Allison Nov 14 '10 at 10:39
  • No many... I suppose there is no guarantee you can get Firefox to treat an image from the web in exactly the same way as it treats an image opened locally. But if (as you seem to say) you have found an image on the web that works the way you want it to, then add the Live HTTP Headers plugin into Firefox, look at the headers, and then duplicate both the headers and the HTML.... that way your image has to do exactly the same thing. If you can't find an image on the web that works the right way - probably means it's not possible. – James Gaunt Nov 14 '10 at 13:01
  • @James: I worked it out and posted my Answer. Thanks for your help. – Mark Allison Nov 14 '10 at 19:05
  • Excellent, good job. Good luck with the rest of the application. – James Gaunt Nov 14 '10 at 21:35
0

Once you have the byte[] from your database

Set the Response.MimeType to the appropriate value such as "image/jpg" for jpeg images Then do a Response.Write or Response.WriteBinary to write the byte[] out to the response.

Optionally set Caching headers if you want the browser to cache the file.

Shiv Kumar
  • 9,599
  • 2
  • 36
  • 38
  • Thanks but how do I display the image? What changes do I need to make to the DisplayImage.aspx? Currently it is displayed in an image control but I don't want to do that. – Mark Allison Nov 11 '10 at 11:13
0

I've managed to work it out. I can remove DisplayImage.aspx completely and just call the image handler directly from my calling page, so my GridView now looks like this:

<script type="text/javascript">
    function ShowImageInNewPage(url_add) {
        window.open(url_add, 'ViewScreenshot', 'resizable=yes,scrollbars = yes');
    }
</script>

<asp:GridView 
 ID="grdTrades" 
 runat="server" 

 <... removed some properties for brevity ...>
 >
 <Columns>
  <asp:CommandField ShowSelectButton="true" ButtonType="Link" SelectText="Select" /> 

  <.. removed some columns for brevity ...>

  <asp:TemplateField HeaderText="Screenshot" >
   <ItemTemplate>
    <input runat="server" visible='<%# Eval("screenshotId") != DBNull.Value %>' type="button" value='View...' onclick='<%# "ShowImageInNewPage(\"App_Handlers/ImageHandler.ashx?screenshotId=" + Eval("screenshotId") + "\")" %>' />
   </ItemTemplate>
  </asp:TemplateField>
 </Columns>
</asp:GridView>

When the user clicks the button, the image loads into a new window and can be resized on the client by dragging the window corner. Thanks for everyone's help on this.

Mark Allison
  • 6,838
  • 33
  • 102
  • 151