views:

765

answers:

2

As you may know we have got a new ActionResult called FileResult in RC1 version of ASP.NET MVC.

Using that, your action methods can return image to browser dynamically. Something like this:

public ActionResult DisplayPhoto(int id)
{
   Photo photo = GetPhotoFromDatabase(id);
   return File(photo.Content, photo.ContentType);
}

In the HTML code, we can use something like this:

<img src="http://mysite.com/controller/DisplayPhoto/657"&gt;

Since the image is returned dynamically, we need a way to cache the returned stream so that we don't need to read the image again from database. I guess we can do it with something like this, I'm not sure:

Response.StatusCode = 304;

This tells the browser that you already have the image in your cache. I just don't know what to return in my action method after setting StatusCode to 304. Should I return null or something?

+3  A: 

Don't use 304 with FileResult. From the spec:

The 304 response MUST NOT contain a message-body, and thus is always terminated by the first empty line after the header fields.

It's not clear what you're trying to do from your question. The server doesn't know what the browser has in its cache. The browser decides that. If you're trying to tell the browser not to re-fetch the image when needed again if it already has a copy, set the response Cache-Control header.

If you need to return 304, use EmptyResult instead.

Craig Stuntz
In first request, I set the ETag property like this:HttpContext.Current.Response.Cache.SetETag (someUniqueValue);In subsequent requests, by reading ETag I know that the image is in cache of browser and therefore I have to return 304
Maysam
Use EmptyResult, not FileResult when returning 304.
Craig Stuntz
+3  A: 

This blog answered the question for me; http://weblogs.asp.net/jeff/archive/2009/07/01/304-your-images-from-a-database.aspx

Basically, you need to read the request header, compare the last modified dates and return 304 if they match, otherwise return the image (with a 200 status) and set the cache headers appropriately.

Code snippet from the blog:

public ActionResult Image(int id)
{
    var image = _imageRepository.Get(id);
    if (image == null)
        throw new HttpException(404, "Image not found");
    if (!String.IsNullOrEmpty(Request.Headers["If-Modified-Since"]))
    {
        CultureInfo provider = CultureInfo.InvariantCulture;
        var lastMod = DateTime.ParseExact(Request.Headers["If-Modified-Since"], "r", provider).ToLocalTime();
        if (lastMod == image.TimeStamp.AddMilliseconds(-image.TimeStamp.Millisecond))
        {
            Response.StatusCode = 304;
            Response.StatusDescription = "Not Modified";
            return Content(String.Empty);
        }
    }
    var stream = new MemoryStream(image.GetImage());
    Response.Cache.SetCacheability(HttpCacheability.Public);
    Response.Cache.SetLastModified(image.TimeStamp);
    return File(stream, image.MimeType);
}
WildJoe