3
\$\begingroup\$

When Page_Load is executed I assign the ImageUrl which is a IHTTPHandler:

imgView.ImageUrl = "ImageHandler.ashx" + "?rezeptId=" + rezeptId.ToString() +"&imageId=0";

Which looks like this:

public class ImageHandler : IHttpHandler {

    public void ProcessRequest (HttpContext context) {

        int rezeptId = Int32.Parse(context.Request.QueryString["rezeptId"]);
        int imageId = Int32.Parse(context.Request.QueryString["imageId"]);

        List<Image> images = DBManager.GetRezeptImages(rezeptId);
        context.Response.ContentType = "image/png";
        context.Response.BinaryWrite(Helper.ImageToByteArray(images[imageId]));
    }

    public bool IsReusable {
        get {
            return false;
        }
    }

}

The handler call is a static method from DBManager, a custom static class which provides LINQ-To-SQL queries. The method is called GetRezeptImages():

 public static List<Image> GetRezeptImages(int rezeptId)
    {
        using (CookBookDataContext ctx = new CookBookDataContext(Resources.ResourceFile.DBConnection))
        {
            IEnumerable<RezeptBilder> bilder = from b in ctx.RezeptBilders where b.FKRezept == rezeptId select b;
            List<Image> imageList = new List<Image>();

            foreach (RezeptBilder b in bilder)
            {
                imageList.Add(Helper.ByteArrayToImage(b.Bild.ToArray()));
            }

            return imageList;
        }
    }

This method simply loads image binaries from the database and converts them to Images:

public static Image ByteArrayToImage(byte[] byteArrayIn)
{
    using (MemoryStream ms = new MemoryStream(byteArrayIn))
    {
        Image returnImage = Image.FromStream(ms);
        return returnImage;
    }
}

So basically this is all that happens when loading images. It is possible that GetRezeptImages() will return a List with more than one image but it is always only one that is loaded to image control.

But now I am experiencing some delays when loading an image:

Clearly taking to long for one image!

Now i wonder what the reason is, here are some things I think could be the reason:

  1. Images have to be compressed
  2. My method implementation requires re-engineering.
  3. Images are simply to big, which is somehow the same as they need to be compressed.
  4. I am using a trial that my Webhoster provides. I haven't figured out if there is some speed limitations or somkething yet.

So I was hoping that someone with more experience in webdevelopement could give me some advice on this issue.

\$\endgroup\$
  • 1
    \$\begingroup\$ Why not only get the image you need from the db? \$\endgroup\$ – Ben Aaronson Nov 21 '14 at 12:37
  • \$\begingroup\$ @BenAaronson because I am in testing phase right now. But it is planned to implement a slideshow or something similar so I wanted the method to return multiple images. \$\endgroup\$ – チーズパン Nov 21 '14 at 12:39
3
\$\begingroup\$

So, let us see what you are doing.

You query your database for IEnumerable<RezeptBilder> and iterate over the enumeration where you are converting the items to Image and add it to a List<Image>.
Then you are converting the Image back to a Byte[] to write it into the response stream.

Byte[] -> Image -> Byte[] isn't really necessary.

As you don't use methods of the List<T> but rather of the interface IList<T> we will return an IList<T> as it is always better to code against an interface instead of an implementation.

public static IList<Byte[]> GetRezeptImages(int rezeptId)
{
    using (CookBookDataContext ctx = new CookBookDataContext(Resources.ResourceFile.DBConnection))
    {
        IEnumerable<RezeptBilder> bilder = from b in ctx.RezeptBilders where b.FKRezept == rezeptId select b;
        IList<Byte[]> imageList = new List<Byte[]>();

        foreach (RezeptBilder b in bilder)
        {
            imageList.Add(b.Bild.ToArray());
        }

        return imageList;
    }
}

and to be called

public void ProcessRequest (HttpContext context) 
{
    int rezeptId = Int32.Parse(context.Request.QueryString["rezeptId"]);
    int imageId = Int32.Parse(context.Request.QueryString["imageId"]);

    IList<Byte[]> images = DBManager.GetRezeptImages(rezeptId);
    context.Response.ContentType = "image/png";
    context.Response.BinaryWrite(images[imageId]);
}  

But this shouldn't fix your issue. Taking 28 seconds for accessing 1.3 MB comes down to about 48 Kbyte/s which is quit slow.

To check where the most time is used, you need to measure the time it takes to execute the call to GetRezeptImages().

Smaller images will for sure speed the whole thing up and therefor makes the user of the website happy.

General

What will happen if a user of the site is changing the image url inside the address bar like ImageHandler.ashx?rezeptId=testForTryParse&imageId=0 ?

Your ProcessRequest() method would throw an exception. You should carefully validate the input. This can be done in this case by using Int32.TryParse() instead of Int32.Parse().

Also you will get an exception, if the requested imageId is greater than the count of the IList.

public class ImageHandler : IHttpHandler 
{

    public void ProcessRequest (HttpContext context) 
    {
        int rezeptId = 0;
        int imageId = 0;

        if( Int32.TryParse(context.Request.QueryString["rezeptId"], out rezeptId) &&
             Int32.TryParse(context.Request.QueryString["imageId"], out imageId))
        {

            IList<Byte[]> images = DBManager.GetRezeptImages(rezeptId);
            if (images.Count > imageId)
            {
                context.Response.ContentType = "image/png";
                context.Response.BinaryWrite(images[imageId]);
                return;
            }
        }
        // a proper defined error response here    
    }

    public bool IsReusable 
    {
        get 
        {
            return false;
        }
    }

}

Style

You should be consistent with your coding style. It is a matter of taste where to place the opening braces {. Most do it on a new line, some place it on the same line. But you should stick to it.

\$\endgroup\$
  • \$\begingroup\$ Thank you, that was very useful! About the braces thing, the ImageHandler code was generated automatically. This is the reason why it does not contain the braces as the other methods/classes do. \$\endgroup\$ – チーズパン Nov 28 '14 at 10:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.