Mark Allison
Mark Allison

Reputation: 7228

How to load an image in ASP.NET from a database as a file in a web browser?

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...

Upvotes: 2

Views: 9753

Answers (4)

Mark Allison
Mark Allison

Reputation: 7228

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.

Upvotes: 0

James Gaunt
James Gaunt

Reputation: 14783

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.

Upvotes: 1

Shiv Kumar
Shiv Kumar

Reputation: 9799

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.

Upvotes: 0

Klaus Byskov Pedersen
Klaus Byskov Pedersen

Reputation: 120917

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.

Upvotes: 3

Related Questions