I ended up using @thinkzig's solution, albeit in a slightly different fashion. Using the FileContent from the MVC Futures assembly, I just added another route to handle images.
routes.MapRoute("Image", "article/{id}/{image}", new { controller = "Article", action = "Image" });
routes.MapRoute("Article", "article/{id}", new { controller = "Article", action = "Index" });
This new action method simply constructs the filepath based on the articleID and image name:
public ActionResult Image(string id, string image)
{
string articlePath = Server.MapPath("~/views/article/");
string filePath = Path.Combine(articlePath, string.Format("{0}/{1}", id, image));
return this.File(filePath, "image");
}
There was one other little thing I had to contend with. If the user accesses the article without a trailing slash (http://localhost/article/8AB98
), then the browser thinks that the articleID is the file, and tries to find the image wrong folder (http://localhost/article/img.png
).
Thankfully in that case, mvc routes the request to the Article action with the image name as the "id" parameter, so I can simply look for a "." in the id and then use the regular Image action to process it.
in the Article action:
if (id.Contains("."))
{
return RedirectToImage(id);
}
And then the redirecttoimage code which figures out the ID and file name
private ActionResult RedirectToImage(string id)
{
if (Request.UrlReferrer == null)
{
return Content("invalid request");
}
var referrer = Request.UrlReferrer.ToString();
if (referrer.Contains("?"))
{
referrer = referrer.Split('?')[0];
}
var realId = Path.GetFileName(referrer);
return this.Image(realId, id);
}
You'll note that I rely on the url referrer to get the actual article ID. If the user tries to right click on the image (when viewed without the trailing slash) and chooses "open image in new tab", then I have no way of knowing what the article ID is, so I just return an "invalid request" string to the user. That's fine because I'm not really trying to support those users in that situation :-)