¿Puede un controlador ASP.NET MVC devolver una imagen?
¿Puedo crear un controlador que simplemente devuelva un recurso de imagen?
Me gustaría enrutar esta lógica a través de un controlador, siempre que se solicite una URL como la siguiente:
www.mywebsite.com/resource/image/topbanner
El controlador buscará topbanner.png
y enviará esa imagen directamente al cliente.
He visto ejemplos de esto en los que tienes que crear una Vista; no quiero usar una Vista. Quiero hacerlo todo solo con el controlador.
es posible?
Utilice el método de archivo de controladores base.
public ActionResult Image(string id)
{
var dir = Server.MapPath("/Images");
var path = Path.Combine(dir, id + ".jpg"); //validate the path for security or use other means to generate the path.
return base.File(path, "image/jpeg");
}
Como nota, esto parece ser bastante eficiente. Hice una prueba donde solicité la imagen a través del controlador ( http://localhost/MyController/Image/MyImage
) y a través de la URL directa ( http://localhost/Images/MyImage.jpg
) y los resultados fueron:
- MVC: 7,6 milisegundos por foto
- Directo: 6,7 milisegundos por foto
Nota: este es el tiempo promedio de una solicitud. El promedio se calculó realizando miles de solicitudes en la máquina local, por lo que los totales no deben incluir la latencia de la red ni los problemas de ancho de banda.
Usando la versión de lanzamiento de MVC, esto es lo que hago:
[AcceptVerbs(HttpVerbs.Get)]
[OutputCache(CacheProfile = "CustomerImages")]
public FileResult Show(int customerId, string imageName)
{
var path = string.Concat(ConfigData.ImagesDirectory, customerId, "\\", imageName);
return new FileStreamResult(new FileStream(path, FileMode.Open), "image/jpeg");
}
Obviamente tengo algunas cosas específicas de la aplicación aquí con respecto a la construcción de la ruta, pero la devolución de FileStreamResult es agradable y simple.
Hice algunas pruebas de rendimiento con respecto a esta acción en comparación con su llamada diaria a la imagen (sin pasar por el controlador) y la diferencia entre los promedios fue de solo aproximadamente 3 milisegundos (el promedio del controlador fue de 68 ms, el promedio sin controlador fue de 65 ms).
Probé algunos de los otros métodos mencionados en las respuestas aquí y el impacto en el rendimiento fue mucho más dramático... varias de las respuestas de las soluciones fueron hasta 6 veces mayores que las de los no controladores (otros controladores promediaron 340 ms, los no controladores 65 ms).
Para explicar ligeramente la respuesta de Dyland:
Tres clases implementan la clase FileResult :
System.Web.Mvc.FileResult
System.Web.Mvc.FileContentResult
System.Web.Mvc.FilePathResult
System.Web.Mvc.FileStreamResult
Todos se explican por sí mismos:
- Para descargas de rutas de archivos donde el archivo existe en el disco, utilice
FilePathResult
: esta es la forma más sencilla y evita tener que utilizar Streams. - Para matrices de bytes[] (similares a Response.BinaryWrite), utilice
FileContentResult
. - Para matrices de bytes [] donde desea descargar el archivo (disposición de contenido: archivo adjunto), utilícelas
FileStreamResult
de manera similar a la siguiente, pero conMemoryStream
y usandoGetBuffer()
. - Para
Streams
usarFileStreamResult
. Se llama FileStreamResult pero requiere un archivo,Stream
así que supongo que funciona con un archivoMemoryStream
.
A continuación se muestra un ejemplo del uso de la técnica de disposición de contenido (no probado):
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult GetFile()
{
// No need to dispose the stream, MVC does it for you
string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "App_Data", "myimage.png");
FileStream stream = new FileStream(path, FileMode.Open);
FileStreamResult result = new FileStreamResult(stream, "image/png");
result.FileDownloadName = "image.png";
return result;
}
Esto puede resultar útil si desea modificar la imagen antes de devolverla:
public ActionResult GetModifiedImage()
{
Image image = Image.FromFile(Path.Combine(Server.MapPath("/Content/images"), "image.png"));
using (Graphics g = Graphics.FromImage(image))
{
// do something with the Graphics (eg. write "Hello World!")
string text = "Hello World!";
// Create font and brush.
Font drawFont = new Font("Arial", 10);
SolidBrush drawBrush = new SolidBrush(Color.Black);
// Create point for upper-left corner of drawing.
PointF stringPoint = new PointF(0, 0);
g.DrawString(text, drawFont, drawBrush, stringPoint);
}
MemoryStream ms = new MemoryStream();
image.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
return File(ms.ToArray(), "image/png");
}
Puedes crear tu propia extensión y hacerlo de esta manera.
public static class ImageResultHelper
{
public static string Image<T>(this HtmlHelper helper, Expression<Action<T>> action, int width, int height)
where T : Controller
{
return ImageResultHelper.Image<T>(helper, action, width, height, "");
}
public static string Image<T>(this HtmlHelper helper, Expression<Action<T>> action, int width, int height, string alt)
where T : Controller
{
var expression = action.Body as MethodCallExpression;
string actionMethodName = string.Empty;
if (expression != null)
{
actionMethodName = expression.Method.Name;
}
string url = new UrlHelper(helper.ViewContext.RequestContext, helper.RouteCollection).Action(actionMethodName, typeof(T).Name.Remove(typeof(T).Name.IndexOf("Controller"))).ToString();
//string url = LinkBuilder.BuildUrlFromExpression<T>(helper.ViewContext.RequestContext, helper.RouteCollection, action);
return string.Format("<img src=\"{0}\" width=\"{1}\" height=\"{2}\" alt=\"{3}\" />", url, width, height, alt);
}
}
public class ImageResult : ActionResult
{
public ImageResult() { }
public Image Image { get; set; }
public ImageFormat ImageFormat { get; set; }
public override void ExecuteResult(ControllerContext context)
{
// verify properties
if (Image == null)
{
throw new ArgumentNullException("Image");
}
if (ImageFormat == null)
{
throw new ArgumentNullException("ImageFormat");
}
// output
context.HttpContext.Response.Clear();
context.HttpContext.Response.ContentType = GetMimeType(ImageFormat);
Image.Save(context.HttpContext.Response.OutputStream, ImageFormat);
}
private static string GetMimeType(ImageFormat imageFormat)
{
ImageCodecInfo[] codecs = ImageCodecInfo.GetImageEncoders();
return codecs.First(codec => codec.FormatID == imageFormat.Guid).MimeType;
}
}
public ActionResult Index()
{
return new ImageResult { Image = image, ImageFormat = ImageFormat.Jpeg };
}
<%=Html.Image<CapchaController>(c => c.Index(), 120, 30, "Current time")%>