0

My web application allow user send report and also to insert up to 5 images into database (SQL Server) as varbinary(MAX). I am now finding a way to retrieve all images of a particular report a user sent. Previously, I used to retrieve image 1 by 1 through a aspx page. But I faced a problem when the user only insert 1 image out of the 5. My 4 other image tag will be displaying null image. So now I trying to find a method if I can retrieve those images based on the report in just 1 image tag. Image was stored in the same table as the report previously, now I change it to 2 table. MemberReport is all details of the report and MemberReportImage is all the images of the report.

This code below allow me to retrieve the first image out of my MemberReportImage table based on the memberreportid into a image tag, however I wish to retrieve all the image with the same memberreportid at once than into the image tag. As what I think it required a loop to get all images but I not sure how to do. So I need someone to help me out on this. THANKS!

protected void Page_Load(object sender, EventArgs e)
{
    string strQuery = "select image from MemberReportImage where memberreportid='" + Session["memberreportid"] + "'";
    SqlCommand cmd = new SqlCommand(strQuery);
    DataTable dt = GetData(cmd);
    if (dt != null)
    {
        download(dt);
    }
}

private DataTable GetData(SqlCommand cmd)
{
    DataTable dt = new DataTable();
    SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString);
    SqlDataAdapter sda = new SqlDataAdapter();
    cmd.CommandType = CommandType.Text;
    cmd.Connection = con;
    try
    {
        con.Open();
        sda.SelectCommand = cmd;
        sda.Fill(dt);
        return dt;
    }
    catch
    {
        return null;
    }
    finally
    {
        con.Close();
        sda.Dispose();
        con.Dispose();
    }
}

private void download(DataTable dt)
{
    // check if you have any rows at all 
    // no rows -> no data to convert!
    if (dt.Rows.Count <= 0)
        return;

    // check if your row #0 even contains data -> if not, you can't do anything!
    if (dt.Rows[0].IsNull("image"))
        return;

    Byte[] bytes = (Byte[])dt.Rows[0]["image"];
    Response.Buffer = true;
    Response.Charset = "";
    Response.Cache.SetCacheability(HttpCacheability.NoCache);
    Response.ContentType = "image/jpg";
    Response.BinaryWrite(bytes);
    Response.End();
}
XiAnG
  • 245
  • 2
  • 9
  • 25
  • 1
    See http://stackoverflow.com/questions/13104356/display-varbinary-as-a-downloadable-link/13104410#13104410 – Tim M. Sep 11 '13 at 03:32

1 Answers1

2

There are many examples of doing what you ask out there. This answer is broken into two parts. First you need to (on your UI page) figure out which images exist and show \ hide the images based on the results of the query. Next you need to use a page (or more appropriately an HttpHandler) to display the image on the page. I have mocked up a quick sample below. I have tried to comment the code to help you read through it.

Your ASPx page (Html)

<form id="form1" runat="server">
    <div>
        <asp:Image ID="Image1" runat="server" />
        <asp:Image ID="Image2" runat="server" />
        <asp:Image ID="Image3" runat="server" />
        <asp:Image ID="Image4" runat="server" />
        <asp:Image ID="Image5" runat="server" />
    </div>
</form>

This is a simple page with 5 images. Your page will be quite a bit more complex but this is just for demo purposes. Next we will use the page-load event (or any other event) to look-up the images from the database and hide the images that are not uploaded.

The ASPx page (code)

protected void Page_Load(object sender, EventArgs e)
{
    Session["memberreportid"] = 1; //TODO: Remove this
    var query = "SELECT TOP 1 * FROM  MemberReport where memberreportid=@p1";

    //create the format string for each image. Note the last variable is {0} for additional formating
    string imageSource = string.Format("/viewImage.ashx?memberreportid={0}&imageID={1}", Session["memberreportid"], "{0}");

    //set our URL to our Image handler for each image
    Image1.ImageUrl = string.Format(imageSource, 1);
    Image2.ImageUrl = string.Format(imageSource, 2);
    Image3.ImageUrl = string.Format(imageSource, 3);
    Image4.ImageUrl = string.Format(imageSource, 4);
    Image5.ImageUrl = string.Format(imageSource, 5);

    //execute our command. Note we are using parameters in our SQL to circumvent SQL injection
    using (var con = new SqlConnection(ConfigurationManager.ConnectionStrings["conString"].ConnectionString))
    {
        var cmd = new SqlCommand(query, con);
        cmd.CommandType = System.Data.CommandType.Text;
        cmd.CommandTimeout = 3000;
        cmd.Parameters.AddWithValue("@p1", Session["memberreportid"]);
        con.Open();
        var reader = cmd.ExecuteReader();
        while (reader.Read())
        {
            //hide each image if the image is null or not a byte array (should always be a byte array)

            if (reader["image1"] == null || !(reader["image1"] is byte[]))
                Image1.Visible = false;
            if (reader["image2"] == null || !(reader["image2"] is byte[]))
                Image2.Visible = false;
            if (reader["image3"] == null || !(reader["image3"] is byte[]))
                Image3.Visible = false;
            if (reader["image4"] == null || !(reader["image4"] is byte[]))
                Image4.Visible = false;
            if (reader["image5"] == null || !(reader["image5"] is byte[]))
                Image5.Visible = false;

            //we only want the first row so break (should never happen anyway)
            break;
        }
        con.Close();
    }
}

As you can see from the above we are simply doing a query to find which images are uploaded based on the ID found. If the image is null (or not a byte[]) then the image control is hidden.

The final piece is using a HttpHandler (GenericHandler from the New Item list). The HttpHandler is probably your best bet here as you don't need all the page generation events and can hook directly into the HttpHandler context. From here you can do pretty much everything you would like to do in an ASPx page (my personal implementation). Note by default the HttpHandler DOES NOT have access to the Session state.

Add a new GenericHandler called viewImage.ashx (or whichever you wish) and add the following code. Again this is commented to help read through.

viewImage.ashx code.

using System;
using System.Configuration;
using System.Data.SqlClient;
using System.Web;

namespace WebApplication1
{
    /// <summary>
    /// Summary description for viewImage
    /// </summary>
    public class viewImage : IHttpHandler
    {
        /// <summary>
        /// process the request
        /// </summary>
        /// <param name="context">the current request handler</param>
        public void ProcessRequest(HttpContext context)
        {
            ///extract our params from the request
            int memberID = 0, imageID = 0;
            if (!int.TryParse(context.Request["memberreportid"], out memberID) || 
                 memberID <= 0 ||
                 !int.TryParse(context.Request["imageID"], out imageID) || 
                 imageID <= 0)
            {
                this.transmitError();
                return;
            }

            //build our query
            var query = string.Format("SELECT TOP 1 image{0} FROM  MemberReport where memberreportid=@p1", imageID);

            //execute the query
            using (var con = new SqlConnection(ConfigurationManager.ConnectionStrings["conString"].ConnectionString))
            {
                try
                {
                    var cmd = new SqlCommand(query, con);
                    cmd.CommandType = System.Data.CommandType.Text;
                    cmd.CommandTimeout = 3000;
                    //set the member command type
                    cmd.Parameters.AddWithValue("@p1", memberID);
                    con.Open();
                    var reader = cmd.ExecuteReader();
                    while (reader.Read())
                    {
                        string psudoFileName = string.Format("memberReport_{0}_image{1}.png", memberID, imageID);
                        byte[] binary = reader[0] as byte[];
                        context.Response.ContentType = "image/png";
                        context.Response.AppendHeader("Content-Disposition", string.Format("filename=\"{0}\"", psudoFileName));
                        context.Response.AppendHeader("Content-Length", binary.Length.ToString());
                        context.Response.BinaryWrite(binary);

                        //todo: Implement your caching. we will use no caching
                        context.Response.Cache.SetCacheability(HttpCacheability.NoCache);

                        //we only want the first row so break (should never happen anyway)
                        break;
                    }
                    con.Close();
                }
                catch (Exception ex)
                {
                    //TODO: Maybe some logging?

                }
                this.transmitError();   
            }
        }

        /// <summary>
        /// transmits a non-image found
        /// </summary>
        void transmitError()
        {
            var context = HttpContext.Current;
            if (context == null)
                return;
            //set the response type
            context.Response.ContentType = "image/png";
            //set as no-cache incase this image path works in the future.
            context.Response.Cache.SetCacheability(HttpCacheability.NoCache);

            //transmit the no-image found error
            context.Response.TransmitFile(context.Server.MapPath("no-image.png"));
        }

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

Now this should be all you need to display the images on the page and be-able to show \ hide the images as they are uploaded.

Hope this helps.

BenMorel
  • 34,448
  • 50
  • 182
  • 322
Nico
  • 12,493
  • 5
  • 42
  • 62
  • Sorry I don't really get how it work. This my first time doing this, I never use GenericHandler(.ashx) before too. Maybe I should change my question. – XiAnG Sep 11 '13 at 03:29
  • You dont have to use an ASHx handler, you could use an ASPx page. Basically you set the image url to the handler. The handler will pass the values in the query string (in this example). From here you simply parse out the member id and the image ID and send the image from the database as a byte array to the Http Response. – Nico Sep 11 '13 at 03:32