Question

Puis-je créer un contrôleur qui renvoie simplement un élément d'image?

Je souhaite router cette logique via un contrôleur, chaque fois qu'une adresse URL telle que celle-ci est demandée:

www.mywebsite.com/resource/image/topbanner

Le contrôleur va rechercher topbanner.png et renvoyer cette image directement au client.

J'ai vu des exemples de ce type dans lesquels vous devez créer une vue. Je ne souhaite pas utiliser une vue. Je veux tout faire avec seulement le contrôleur.

Est-ce possible?

Était-ce utile?

La solution

Utilisez la méthode Fichier des contrôleurs de 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");
}

Comme note, cela semble être assez efficace. J'ai fait un test où j'ai demandé l'image via le contrôleur ( http: // localhost / MyController / Image / MyImage ) et via l'URL directe ( http: // localhost / Images / MyImage .jpg ) et les résultats sont les suivants:

  • MVC: 7,6 millisecondes par photo
  • Direct: 6,7 millisecondes par photo

Remarque: il s’agit du temps moyen d’une demande. La moyenne a été calculée en effectuant des milliers de demandes sur l'ordinateur local. Par conséquent, les totaux ne doivent pas inclure les problèmes de latence du réseau ou de bande passante.

Autres conseils

En utilisant la version finale de MVC, voici ce que je fais:

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

J’ai bien évidemment quelques éléments spécifiques à l’application concernant la construction du chemin, mais le retour de FileStreamResult est simple et agréable.

J'ai effectué quelques tests de performance en ce qui concerne cette action contre votre appel quotidien à l'image (en contournant le contrôleur) et la différence entre les moyennes n'était que d'environ 3 millisecondes (la moyenne moyenne était de 68 ms, la moyenne était de 65 ms).

J'avais déjà essayé certaines des méthodes mentionnées dans les réponses précédentes et l'impact sur les performances était bien plus dramatique ... plusieurs des solutions proposées étaient autant que 6x les non-contrôleurs (autres contrôleurs moy. 340 ms, non contrôleurs 65 ms ).

Pour expliquer légèrement la réponse de Dyland:

Trois classes implémentent la classe FileResult . :

System.Web.Mvc.FileResult
      System.Web.Mvc.FileContentResult
      System.Web.Mvc.FilePathResult
      System.Web.Mvc.FileStreamResult

Ils sont tous assez explicites:

  • Pour les téléchargements de chemin de fichier où le fichier existe sur le disque, utilisez FilePathResult - c'est le moyen le plus simple et vous évite d'avoir à utiliser Streams.
  • Pour les tableaux d'octets [] (semblables à Response.BinaryWrite), utilisez FileContentResult .
  • Pour les tableaux d'octets [] dans lesquels vous souhaitez télécharger le fichier (content-disposition: attachment), utilisez FileStreamResult de la même manière que ci-dessous, mais avec un MemoryStream . et en utilisant GetBuffer () .
  • Pour Streams , utilisez FileStreamResult . Il s’agit d’un FileStreamResult mais il faut un Stream pour que devine cela fonctionne avec un MemoryStream .

Voici un exemple d'utilisation de la technique de disposition de contenu (non testée):

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

Cela peut être utile si vous souhaitez modifier l'image avant de la renvoyer:

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

Vous pouvez créer votre propre extension et procéder de cette manière.

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")%>

Vous pouvez écrire directement dans la réponse, mais celle-ci n'est pas testable. Il est préférable de renvoyer un ActionResult qui a différé l'exécution. Voici mon StreamResult resusable:

public class StreamResult : ViewResult
{
    public Stream Stream { get; set; }
    public string ContentType { get; set; }
    public string ETag { get; set; }

    public override void ExecuteResult(ControllerContext context)
    {
        context.HttpContext.Response.ContentType = ContentType;
        if (ETag != null) context.HttpContext.Response.AddHeader("ETag", ETag);
        const int size = 4096;
        byte[] bytes = new byte[size];
        int numBytes;
        while ((numBytes = Stream.Read(bytes, 0, size)) > 0)
            context.HttpContext.Response.OutputStream.Write(bytes, 0, numBytes);
    }
}

Pourquoi ne pas faire simple et utiliser l'opérateur tilde ~ ?

public FileResult TopBanner() {
  return File("~/Content/images/topbanner.png", "image/png");
}

UPDATE: Il existe de meilleures options que ma réponse d'origine. Cela fonctionne très bien en dehors de MVC, mais il est préférable de s'en tenir aux méthodes intégrées de renvoi du contenu de l'image. Voir les réponses positives.

Vous pouvez certainement. Essayez ces étapes:

  1. Chargez l'image du disque dans un tableau d'octets
  2. mettez en cache l'image au cas où vous vous attendiez à plus de demandes d'image et que vous ne souhaitiez pas que l'entrée / la sortie du disque soit effectuée (mon échantillon ne le met pas en cache ci-dessous)
  3. Modifiez le type mime via le Response.ContentType
  4. Response.BinaryEcrit le tableau d'octets d'image

Voici un exemple de code:

string pathToFile = @"C:\Documents and Settings\some_path.jpg";
byte[] imageData = File.ReadAllBytes(pathToFile);
Response.ContentType = "image/jpg";
Response.BinaryWrite(imageData);

J'espère que ça aide!

Solution 1: pour rendre une image dans une vue à partir d'une URL d'image

Vous pouvez créer votre propre méthode d'extension:

public static MvcHtmlString Image(this HtmlHelper helper,string imageUrl)
{
   string tag = "<img src='{0}'/>";
   tag = string.Format(tag,imageUrl);
   return MvcHtmlString.Create(tag);
}

Ensuite, utilisez-le comme:

@Html.Image(@Model.ImagePath);

Solution 2: restitution de l'image à partir de la base de données

Créez une méthode de contrôleur qui renvoie les données d'image comme ci-dessous

public sealed class ImageController : Controller
{
  public ActionResult View(string id)
  {
    var image = _images.LoadImage(id); //Pull image from the database.
    if (image == null) 
      return HttpNotFound();
    return File(image.Data, image.Mime);
  }
}

Et utilisez-le dans une vue telle que:

@ { Html.RenderAction("View","Image",new {id=@Model.ImageId})}

Pour utiliser une image générée à partir de cette action dans un code HTML quelconque, utilisez

<img src="http://something.com/image/view?id={imageid}>

Cela a fonctionné pour moi. Depuis que je stocke des images sur une base de données SQL Server.

    [HttpGet("/image/{uuid}")]
    public IActionResult GetImageFile(string uuid) {
        ActionResult actionResult = new NotFoundResult();
        var fileImage = _db.ImageFiles.Find(uuid);
        if (fileImage != null) {
            actionResult = new FileContentResult(fileImage.Data,
                fileImage.ContentType);
        }
        return actionResult;
    }

Dans l'extrait de code ci-dessus, _db.ImageFiles.Find (uuid) recherche l'enregistrement de fichier image dans la base de données (contexte EF). Il renvoie un objet FileImage, qui est simplement une classe personnalisée que j'ai créée pour le modèle, puis l'utilise comme FileContentResult.

public class FileImage {
   public string Uuid { get; set; }
   public byte[] Data { get; set; }
   public string ContentType { get; set; }
}

Regardez ContentResult. Ceci retourne une chaîne, mais peut être utilisé pour créer votre propre classe similaire à BinaryResult.

vous pouvez utiliser Fichier pour retourner un fichier tel que Voir, Contenu, etc.

 public ActionResult PrintDocInfo(string Attachment)
            {
                string test = Attachment;
                if (test != string.Empty || test != "" || test != null)
                {
                    string filename = Attachment.Split('\\').Last();
                    string filepath = Attachment;
                    byte[] filedata = System.IO.File.ReadAllBytes(Attachment);
                    string contentType = MimeMapping.GetMimeMapping(Attachment);

                    System.Net.Mime.ContentDisposition cd = new System.Net.Mime.ContentDisposition
                    {
                        FileName = filename,
                        Inline = true,
                    };

                    Response.AppendHeader("Content-Disposition", cd.ToString());

                    return File(filedata, contentType);          
                }
                else { return Content("<h3> Patient Clinical Document Not Uploaded</h3>"); }

            }

Je vois deux options:

1) Implémentez votre propre IViewEngine et définissez la propriété ViewEngine du contrôleur que vous utilisez sur votre ImageViewEngine dans votre "image" désirée. méthode.

2) Utilisez une vue :-). Il suffit de changer le type de contenu, etc.

Vous pouvez utiliser HttpContext.Response et y écrire directement le contenu (WriteFile () peut fonctionner pour vous), puis renvoyer ContentResult de votre action au lieu d'ActionResult.

Avertissement: je n'ai pas essayé cela, c'est basé sur les API disponibles. : -)

if (!System.IO.File.Exists(filePath))
    return SomeHelper.EmptyImageResult(); // preventing JSON GET/POST exception
else
    return new FilePathResult(filePath, contentType);

SomeHelper.EmptyImageResult () doit renvoyer FileResult avec une image existante (1x1 transparent, par exemple).

Cette méthode est la plus simple si vous avez des fichiers stockés sur le lecteur local. Si les fichiers sont octet [] ou flux , utilisez FileContentResult ou FileStreamResult comme suggéré par Dylan.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top