ASP.Net MVC: Comment afficher une image de tableau d'octets à partir d'un modèle

116

J'ai un modèle avec un fichier image de tableau d'octets que je veux afficher sur la page.

Comment puis-je faire cela sans retourner à la base de données?

Toutes les solutions que je vois utilisent un ActionResultpour revenir à la base de données pour récupérer l'image, mais j'ai déjà l'image sur le modèle ...

NSP ALT
la source
11
Veuillez arrêter de faire référence à «ASP.NET MVC» simplement en tant que «MVC». L'un est un cadre, tandis que l'autre est un modèle de conception indépendant du langage. C'est comme appeler IE - "Internet"
tereško
MVC Comment afficher une image de tableau d'octets à partir d'un modèle lorsque sa valeur nulle?
Chathz

Réponses:

214

Quelque chose comme ça peut fonctionner ...

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

<img src="@imgSrc" />

Comme mentionné dans les commentaires ci-dessous, veuillez utiliser ce qui précède en sachant que bien que cela puisse répondre à votre question, cela ne résoudra peut-être pas votre problème . En fonction de votre problème, cela peut être la solution, mais je n'exclurais pas complètement l'accès à la base de données deux fois.

dav_i
la source
6
Il convient de noter que cela intégrera l'image dans le HTML et contournera plusieurs techniques de mise en cache d'image standard.
Quintin Robinson
@QuintinRobinson Bien que réduire le nombre de demandes:)
dav_i
8
@dav_i Réduisez le nombre de requêtes initiales . Lors de l'optimisation des performances Web, il est extrêmement important de comprendre les mécanismes des serveurs, des plates-formes de contenu intermédiaire et des clients qui demandent et traitent les informations. Je ne veux pas entrer dans des détails extraordinaires dans un commentaire, mais je tiens à souligner la nécessité de vraiment comprendre les implications de l'utilisation d'une telle technique.
Quintin Robinson
1
La réponse est peut-être correcte pour la question, mais je pense que le problème que nous essayons de résoudre a un défaut. Si le problème actuel empêche deux appels à la base de données pour obtenir des données, puis un second pour obtenir l'image, je pense qu'une bien meilleure solution serait d'utiliser un gestionnaire de sorte qui peut implémenter la mise en cache pour réduire la charge globale sur le serveur. Lorsque vous essayez de comprendre pourquoi votre application s'étouffe parce que vous essayez de vider un énorme fichier en utilisant l'encodage Base64 dans le flux de réponse de page, vous souhaiterez que vous ayez réfléchi davantage à la situation dans son ensemble.
Nick Bork
2
Super, j'ai ajouté une méthode sur mon modèle afin de le réutiliser: chaîne publique GetBase64 () {var base64 = Convert.ToBase64String (ContentImage); return String.Format ("données: image / gif; base64, {0}", base64); }
Rodrigo Longo
40

Cela a fonctionné pour moi

<img src="data:image;base64,@System.Convert.ToBase64String(Model.CategoryPicture.Content)" width="80" height="80"/>     
NoloMokgosi
la source
1
celui-ci a fonctionné pour moi aussi, dites gentiment Comment afficher une image de tableau d'octets à partir d'un modèle lorsque sa valeur est nulle?
Chathz
Salut Chathz, je vous suggère de valider le modèle dans le contrôleur avant de passer à la vue. Si le modèle est nul, passez une image par défaut
NoloMokgosi
26

Je recommande quelque chose de ce genre, même si l'image vit à l'intérieur de votre modèle.

Je réalise que vous demandez un moyen direct d'y accéder directement depuis la vue et beaucoup d'autres ont répondu à cela et vous ont dit ce qui ne va pas avec cette approche, c'est donc juste une autre façon de charger l'image de manière asynchrone pour vous et moi pense est une meilleure approche.

Modèle d'échantillon:

[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.
}

Exemple de méthode dans le contrôleur:

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

    byte[] photoBack = item.InternalImage;

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

Vue

@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
la source
2
Qu'est-ce que "return File (...)"? File n'est-il pas une classe statique?
Ben Sewards
3
Ce devrait être un objet FileContentResult ( msdn.microsoft.com/en-us/library/… )
Louie Bacaj
14

Une façon consiste à ajouter ceci à une nouvelle classe c # ou une nouvelle classe 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 + "' />");
    }
}

alors vous pouvez le faire dans n'importe quelle vue

@Html.Image(Model.ImgBytes)
Moji
la source
J'aime vraiment cela le meilleur - le rend propre dans le modèle et aussi dans le fichier .cshtml. GÉNIAL!!
Ken le
10

Si vous pouvez encoder vos octets en base 64, vous pouvez essayer d'utiliser le résultat comme source d'image. Dans votre modèle, vous pouvez ajouter quelque chose comme:

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);
    }
}

Et à votre avis:

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

Si l'image n'est pas si grande, et s'il y a de bonnes chances que vous la réutilisiez souvent, et si vous n'en avez pas trop, et si les images ne sont pas secrètes (ce qui signifie que ce n'est pas grand traiter si un utilisateur pouvait potentiellement voir l'image d'une autre personne) ...

Il y a beaucoup de "si" ici, donc il y a de fortes chances que ce soit une mauvaise idée:

Vous pouvez stocker les octets d'image Cachependant une courte période et faire pointer une balise d'image vers une méthode d'action, qui à son tour lit dans le cache et crache votre image. Cela permettra au navigateur de mettre en cache l'image de manière appropriée.

// 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");
}

Cela a l'avantage supplémentaire (ou est-ce une béquille?) De travailler dans des navigateurs plus anciens, où les images en ligne ne fonctionnent pas dans IE7 (ou IE8 si elles sont supérieures à 32 Ko).

Joe Enos
la source
3

Ceci est une version modifiée de la réponse de Manoj que j'utilise sur un projet. Juste mis à jour pour prendre une classe, des attributs html et utiliser le 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));
    }

Qui peut être utilisé alors comme suit:

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

Vous devez avoir un octet [] dans votre base de données.

Mon octet [] est dans mon objet Person:

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


Vous devez convertir votre octet [] en chaîne. Donc, j'ai dans mon contrôleur:

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


Ensuite, dans mon fichier .cshtml, mon modèle est un ViewModel. Voici ce que j'ai dans:

 public String Image { get; set; }


Je l'utilise comme ça dans mon fichier .cshtml:

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

"data: extension de fichier image / image ; base64, {0}, votre chaîne d'image "

Je souhaite que cela aide quelqu'un!

Thorpe
la source
1

Si vous souhaitez présenter l'image, ajoutez une méthode en tant que classe d'assistance ou au modèle lui-même et autorisez la méthode à convertir l'image du tableau d'octets en un format d'image tel que PNG ou JPG, puis à la convertir en chaîne Base64. Une fois que vous avez cela, liez la valeur base64 sur votre vue au format

"data: image / [extension de type de fichier image] ; base64, [votre chaîne base64 va ici] "

Ce qui précède est attribué à l' attribut de la imgbalise src.

Le seul problème que j'ai avec cela est que la chaîne base64 est trop longue. Donc, je ne le recommanderais pas pour plusieurs modèles affichés dans une vue.

mitch
la source
Vous évoquez un problème et indiquez un scénario de cas d'utilisation recommandant de ne pas, mais pas de solution, à votre scénario de cas d'utilisation. Cela aurait rendu votre réponse beaucoup plus agréable / utile et plus informative.
Ken
0

J'ai créé une méthode d'assistance basée dans la réponse ci-dessous et je suis assez heureux que cette aide puisse aider autant que possible.

Avec un modèle:

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

L'assistant est:

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 vue reçoit un objet: ICollection, vous devez donc l'utiliser dans la vue dans une instruction foreach:

 @foreach (var item in Model)
  @Html.GetBytes(itemP1 => item.Pic1, item.Graphics, "Idtag")
}
José Ortega
la source