3

In Razor I have a loop that gives me results. In that loop, I have a Url.Action that serves an image:

foreach (var i in Model.InventoryList)
        {
        <tr>                
            <td style="text-align: center;"><img alt="@i.ProductCode" src="@Url.Action("GetImageThumbnail", "ServiceRequest", new { ProductCode = i.ProductCode })" onclick="showGallery('@i.ProductCode');" /></td>                
        </tr>
        }

The image served is dependent on if the image for that particular row exists. If it doesn't, an alternate image is served.

public ActionResult GetImageThumbnail(string productCode)
    {            
        string filePath = "path_to_product.png";
        Size size = new Size(80, 50);
        if (!System.IO.File.Exists(filePath))
        {
            filePath = "No_image_available.png";
            size = new Size(50, 50);
        }
        using (MemoryStream ms = new MemoryStream()){
            Image image = Image.FromFile(filePath);
            image = ImageHelpers.ResizeImage(image, size);
            image.Save(ms, ImageHelpers.getImageFormat(filePath));
            return File(ms.ToArray(), ImageHelpers.getContentType(filePath));
        }

    }

Now this is fine, and works pretty fast if there are a few results. If there are thousands of results, there is some definite slowdown in serving all those instances of images. Is there a more efficient way of doing this?

I could be serving less results in the model but that's not what I'm aiming for in this particular question.

javisrk
  • 582
  • 4
  • 19
  • Just venturing a guess but if all the files are on the same physical drive you may be seeing an IO bottleneck. One potential solution may be to cache the existence of the images once every day or few hours or whatever time period you'd expect changes, and then check that cache instead of the file system when serving the images. – Dmitriy Khaykin Jan 27 '14 at 04:34
  • 1
    Like @DavidKhaykin said, cache the images. And cache the *resized* images; that operation is relatively expensive. In the browser, lazy load the images that are actually visible. Consider using a [cacheable request](http://stackoverflow.com/questions/3870726/force-refresh-of-cached-data/3870743#3870743) and setting cache headers to avoid repeat requests for the same image. – Tim M. Jan 27 '14 at 04:49
  • Why don't u Just add one `string` property to `InventoryList` which will hold `url` for corresponding image – Nitin Varpe Jan 27 '14 at 04:57
  • 1
    Caching is the first place to start. But keep in mind that if you're using ASP.NET Session State then you probably want to signal your intent not to read/write from it in `GetImageThumbnail`. That way your action won't be blocked waiting for other requests and can run in parallel (e.g. if the browser makes four requests at a time). It's old, but a basic primer is here: http://odetocode.com/Blogs/scott/archive/2006/05/21/session-state-uses-a-reader-writer-lock.aspx – ta.speot.is Jan 27 '14 at 05:02

1 Answers1

2

Some options I would say -

  • You can DiskCache Plugin of IIS for resized images. Instead of making all the resizing calculations on the fly, save the resized image at the time of image creation itself. Later if you do not want resized image, it can be deleted at any point of time.
  • Some other plugins like SourceMemCache, OutputMemCache and SourceDiskCache also available for IIS in DiskCache plugin itself.
  • Alternatively, you can write all the resized images to cache providers like Redis.io or to Squid
  • Lazy Load images on your page
  • Try to redesign your page, I mean even if it is a gallery page, try to use pagination, so that thousands of images in a gallery will boil down to few tens on a single page.
ramiramilu
  • 17,044
  • 6
  • 49
  • 66