ASP.Net MVC: Cómo mostrar una imagen de matriz de bytes del modelo

115

Tengo un modelo con un archivo de imagen de matriz de bytes que quiero mostrar en la página.

¿Cómo puedo hacer eso sin volver a la base de datos?

Todas las soluciones que veo usan un ActionResultpara volver a la base de datos para recuperar la imagen, pero ya tengo la imagen en el modelo ...

DK ALT
fuente
11
Por favor, deje de referirse a "ASP.NET MVC" simplemente como "MVC". Uno es un marco, mientras que el otro es un patrón de diseño independiente del lenguaje. Es como llamar a IE - "Internet"
tereško
MVC ¿Cómo mostrar una imagen de matriz de bytes del modelo cuando es nulo?
Chathz

Respuestas:

214

Algo como esto puede funcionar ...

@{
    var base64 = Convert.ToBase64String(Model.ByteArray);
    var imgSrc = String.Format("data:image/gif;base64,{0}", base64);
}

<img src="@imgSrc" />

Como se menciona en los comentarios a continuación, utilice lo anterior armado con el conocimiento de que, aunque esto puede responder a su pregunta, es posible que no resuelva su problema . Dependiendo de su problema, esta puede ser la solución, pero no descartaría por completo acceder a la base de datos dos veces.

dav_i
fuente
6
Cabe señalar que esto incrustará la imagen en el HTML y evitará varias técnicas estándar de almacenamiento en caché de imágenes.
Quintin Robinson
@QuintinRobinson Aunque reduzca el número de solicitudes:)
dav_i
8
@dav_i Reducir el número de solicitudes iniciales . Al optimizar el rendimiento de la web, es muy importante comprender la mecánica de los servidores, las plataformas de contenido intermedias y los clientes que solicitan y procesan la información. No quiero entrar en detalles extraordinarios en un comentario, pero quiero enfatizar la necesidad de comprender verdaderamente las implicaciones de usar tal técnica.
Quintin Robinson
1
La respuesta puede ser correcta para la pregunta, pero creo que el problema que estamos tratando de resolver tiene un defecto. Si el problema en cuestión es evitar dos llamadas a la base de datos para obtener datos y luego una segunda para obtener la imagen, creo que una solución mucho mejor sería usar un controlador de tipo que pueda implementar el almacenamiento en caché para reducir la carga general en el servidor. Cuando intente averiguar por qué su aplicación se está ahogando porque está tratando de volcar un archivo enorme usando la codificación Base64 en el flujo de respuesta de la página, deseará haber pensado más en el panorama general.
Nick Bork
2
Genial, agregué un método en mi modelo para poder reutilizarlo: public string GetBase64 () {var base64 = Convert.ToBase64String (ContentImage); return String.Format ("datos: imagen / gif; base64, {0}", base64); }
Rodrigo Longo
40

Esto funcionó para mi

<img src="data:image;base64,@System.Convert.ToBase64String(Model.CategoryPicture.Content)" width="80" height="80"/>     
NoloMokgosi
fuente
1
este también funcionó para mí, por favor diga ¿Cómo mostrar una imagen de matriz de bytes del modelo cuando es nulo?
Chathz
Hola Chathz, te sugiero que valides el modelo en el controlador antes de pasar a la vista. Si el modelo es nulo, pase una imagen predeterminada
NoloMokgosi
26

Recomiendo algo en este sentido, incluso si la imagen vive dentro de su modelo.

Me doy cuenta de que está pidiendo una forma directa de acceder a él directamente desde la vista y muchos otros han respondido eso y le han dicho qué es lo que está mal con ese enfoque, por lo que esta es solo otra forma que cargará la imagen de forma asincrónica para usted y para mí. creo que es un mejor enfoque.

Modelo de muestra:

[Bind(Exclude = "ID")]
public class Item
{
    [Key]
    [ScaffoldColumn(false)]
    public int ID { get; set; }

    public String Name { get; set; }

    public byte[] InternalImage { get; set; } //Stored as byte array in the database.
}

Método de muestra en el controlador:

public async Task<ActionResult> RenderImage(int id)
{
    Item item = await db.Items.FindAsync(id);

    byte[] photoBack = item.InternalImage;

    return File(photoBack, "image/png");
}

Ver

@model YourNameSpace.Models.Item

@{
    ViewBag.Title = "Details";
}

<h2>Details</h2>

<div>
<h4>Item</h4>
<hr />
<dl class="dl-horizontal">
    <img src="@Url.Action("RenderImage", new { id = Model.ID})" />
</dl>
<dl class="dl-horizontal">
    <dt>
        @Html.DisplayNameFor(model => model.Name)
    </dt>

    <dd>
        @Html.DisplayFor(model => model.Name)
    </dd>
</dl>
</div>
Louie Bacaj
fuente
2
¿Qué es "devolver archivo (...)"? ¿No es File una clase estática?
Ben Sewards
3
Debe ser un objeto FileContentResult ( msdn.microsoft.com/en-us/library/… )
Louie Bacaj
13

Una forma es agregar esto a una nueva clase c # o clase HtmlExtensions

public static class HtmlExtensions
{
    public static MvcHtmlString Image(this HtmlHelper html, byte[] image)
    {
        var img = String.Format("data:image/jpg;base64,{0}", Convert.ToBase64String(image));
        return new MvcHtmlString("<img src='" + img + "' />");
    }
}

entonces puedes hacer esto en cualquier vista

@Html.Image(Model.ImgBytes)
Moji
fuente
Realmente me gusta esto más: lo hace limpio en el Modelo y también en el archivo .cshtml. ¡¡EXCELENTE!!
Ken
10

Si puede codificar sus bytes en base 64, puede intentar usar el resultado como fuente de imagen. En su modelo, puede agregar algo como:

public string ImageSource
{
    get
    {
        string mimeType = /* Get mime type somehow (e.g. "image/png") */;
        string base64 = Convert.ToBase64String(yourImageBytes);
        return string.Format("data:{0};base64,{1}", mimeType, base64);
    }
}

Y en tu opinión:

<img ... src="@Model.ImageSource" />
Cᴏʀʏ
fuente
5

Si la imagen no es tan grande, y si hay una buena posibilidad de que la reutilice con frecuencia, y si no tiene demasiadas, y si las imágenes no son secretas (lo que significa que no es grande tratar si un usuario podría ver la imagen de otra persona) ...

Hay muchos "si" aquí, por lo que es muy probable que sea una mala idea:

Puede almacenar los bytes de la imagen Cachedurante un breve período de tiempo y hacer que una etiqueta de imagen apunte hacia un método de acción, que a su vez lee del caché y escupe su imagen. Esto permitirá que el navegador almacene la imagen en caché de manera adecuada.

// In your original controller action
HttpContext.Cache.Add("image-" + model.Id, model.ImageBytes, null,
    Cache.NoAbsoluteExpiration, TimeSpan.FromMinutes(1),
    CacheItemPriority.Normal, null);

// In your view:
<img src="@Url.Action("GetImage", "MyControllerName", new{fooId = Model.Id})">

// In your controller:
[OutputCache(VaryByParam = "fooId", Duration = 60)]
public ActionResult GetImage(int fooId) {
    // Make sure you check for null as appropriate, re-pull from DB, etc.
    return File((byte[])HttpContext.Cache["image-" + fooId], "image/gif");
}

Esto tiene el beneficio adicional (¿o es una muleta?) De trabajar en navegadores más antiguos, donde las imágenes en línea no funcionan en IE7 (o IE8 si son mayores de 32kB).

Joe Enos
fuente
3

Esta es una versión modificada de la respuesta de Manoj que uso en un proyecto. Recién actualizado para tomar una clase, atributos html y usar TagBuilder.

    public static IHtmlString Image(this HtmlHelper helper, byte[] image, string imgclass, 
                                     object htmlAttributes = null)
    {
        var builder = new TagBuilder("img");
        builder.MergeAttribute("class", imgclass);
        builder.MergeAttributes(new RouteValueDictionary(htmlAttributes));

        var imageString = image != null ? Convert.ToBase64String(image) : "";
        var img = string.Format("data:image/jpg;base64,{0}", imageString);
        builder.MergeAttribute("src", img);

        return MvcHtmlString.Create(builder.ToString(TagRenderMode.SelfClosing));
    }

Que se puede utilizar entonces de la siguiente manera:

    @Html.Image(Model.Image, "img-cls", new { width="200", height="200" })
AlexH
fuente
3

Necesita tener un byte [] en su base de datos.

Mi byte [] está en mi objeto Person:

public class Person
{
    public byte[] Image { get; set; }
}


Necesita convertir su byte [] en una cadena. Entonces, tengo en mi controlador:

String img = Convert.ToBase64String(person.Image);


A continuación, en mi archivo .cshtml, mi modelo es un ViewModel. Esto es lo que tengo en:

 public String Image { get; set; }


Lo uso así en mi archivo .cshtml:

<img src="@String.Format("data:image/jpg;base64,{0}", Model.Image)" />

"datos: imagen / extensión de archivo de imagen ; base64, {0}, su cadena de imagen "

¡Deseo que ayude a alguien!

Thorpe
fuente
1

Si desea presentar la imagen, agregue un método como una clase auxiliar o al modelo en sí y permita que el método convierta la imagen de la matriz de bytes a un formato de imagen como PNG o JPG y luego conviértalo a una cadena Base64. Una vez que tenga eso, vincule el valor base64 en su vista en el formato

"data: image / [extensión de tipo de archivo de imagen] ; base64, [su cadena base64 va aquí] "

Lo anterior se asigna al atributo de la imgetiqueta src.

El único problema que tengo con esto es que la cadena base64 es demasiado larga. Por lo tanto, no lo recomendaría para que se muestren varios modelos en una vista.

mitch
fuente
Menciona un problema e indica un escenario de caso de uso que no recomienda, pero no una solución para su caso de uso. Eso habría hecho que tu respuesta fuera mucho más agradable / útil y más informativa.
Ken
0

He creado un método de ayuda basado en la respuesta a continuación y estoy muy contento de que este ayudante pueda ayudar a tantos como sea posible.

Con modelo:

 public class Images
 {
    [Key]
    public int ImagesId { get; set; }
    [DisplayName("Image")]
    public Byte[] Pic1 { get; set; }
  }

El ayudante es:

public static IHtmlString GetBytes<TModel, TValue>(this HtmlHelper<TModel> helper, System.Linq.Expressions.Expression<Func<TModel, TValue>> expression, byte[] array, string Id)
    {
        TagBuilder tb = new TagBuilder("img");
        tb.MergeAttribute("id", Id);
        var base64 = Convert.ToBase64String(array);
        var imgSrc = String.Format("data:image/gif;base64,{0}", base64);
        tb.MergeAttribute("src", imgSrc);
        return MvcHtmlString.Create(tb.ToString(TagRenderMode.SelfClosing));
    }

La vista recibe un objeto: ICollection, por lo que debe usarlo en la vista en una declaración foreach:

 @foreach (var item in Model)
  @Html.GetBytes(itemP1 => item.Pic1, item.Graphics, "Idtag")
}
Jose ortega
fuente