Pregunta

I'm working on a .NET HTTP handler (with IsReusable set to true) to return an image based on a specified ID. The passed URL would look something like this ReadImage.ashx?id=N. Once the handle receives the request it takes the ID and looks in the HTTP cache for the previously base64 encoded image, like this:

byte[] image;
if (context.Cache["image_" + id] != null)
{
    image = Convert.FromBase64String(context.Cache["image_" + id].ToString());
}

If it's not there, it proceeds to get the filename from the database, read the file from disk, encode it to base64 and store the bytes in image and finally cache the image for 20 minutes like this:

else
{
    var dbImage = m_database.Images.Single(i => i.Id.Equals(id));
    string filePath = context.Server.MapPath(string.Format("~/{base path}/{0}", dbImage.Filename));
    using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
    {
        image = new byte[fs.Length];
        fs.Read(image, 0, image.Length);
        context.Cache.Add("image_" + dbImage.Id, Convert.ToBase64String(image), null, Cache.NoAbsoluteExpiration, TimeSpan.FromMinutes(20), CacheItemPriority.Normal, null);
    }
}

Then I finally BinaryWrite my image buffer to the output stream of the current context:

context.Response.ContentType = "image/png";
context.Response.BinaryWrite(image);
context.Response.Flush();

Now, this is all working perfectly fine - but not ALL the time. I'll occasionally get a broken image, but upon refresh the image is (99% of the time) there.

Looking at the network traffic in Chrome's debugger I can see that my handler is returning text/html in cases where the image is broken and not image/png as it should be. I've tried wrapping my handler in a try-catch and recording the exception details, but it doesn't do anything - it just continues to return garbage to the requesting page.

image handler issue

So far I've tried a few things:

  1. Buffering out my image in 4096 chunks.
  2. Creating a Bitmap object and returning this.
  3. Removing the caching completely (it fails slightly more frequently if I do this).
  4. Trying to capture exceptions and record them (it never throws an exception).

Am I missing the obvious here?? I'm a little stumped, anyone have any advice?

¿Fue útil?

Solución 2

Not so much a solution, but a better workaround.

Instead of using a generic handler, I decided to create an MVC action for it. Here is the action, I've not had a single failure and it seems to be much faster.

[HttpGet]
public ActionResult GetImage(int id)
{
    byte[] buffer;
    if (HttpContext.Cache["image_" + id] == null)
    {
        var dbMediaItem = m_database.MediaItems.Single(i => i.Id.Equals(id));
        string path = string.Format(Server.MapPath("~/{base path}/{0}"), dbMediaItem.Filename);
        using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read))
        {
            buffer = new byte[fs.Length];
            fs.Read(buffer, 0, buffer.Length);
            HttpContext.Cache.Add("image_" + id, Convert.ToBase64String(buffer), null, Cache.NoAbsoluteExpiration, TimeSpan.FromMinutes(20), CacheItemPriority.Normal, null);
        }
    }
    else
    {
        buffer = Convert.FromBase64String(HttpContext.Cache["image_" + id].ToString());
    }
    return base.File(buffer, "image/png");
}

Otros consejos

Just my guess. The problem my lie in the following code:

if (context.Cache["image_" + id] != null)
{
    image = Convert.FromBase64String(context.Cache["image_" + id].ToString());
}

Cache may become invalid at any time. It's very possible that after the if statement evaluates to true, the cache item immediately becomes invalid and eventually the 2nd get will get null. It's better to do this (not 100% sure):

var base64String = context.Cache["image_" + id].ToString();
if (base64String != null) image = ...
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top