¿Puede un controlador ASP.NET MVC devolver una imagen?

Resuelto Jonathan asked hace 16 años • 20 respuestas

¿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.pngy 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?

Jonathan avatar Oct 09 '08 12:10 Jonathan
Aceptado

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.

Brian avatar Aug 28 '2009 20:08 Brian

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).

Sailing Judo avatar Apr 15 '2009 16:04 Sailing Judo

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 FileStreamResultde manera similar a la siguiente, pero con MemoryStreamy usando GetBuffer().
  • Para Streamsusar FileStreamResult. Se llama FileStreamResult pero requiere un archivo, Streamasí que supongo que funciona con un archivo MemoryStream.

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;
    }
Chris S avatar Sep 29 '2009 21:09 Chris S

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");
}
staromeste avatar Mar 28 '2011 11:03 staromeste

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")%>
Oleksandr Fentsyk avatar Jun 09 '2011 06:06 Oleksandr Fentsyk