Can an ASP.NET le contrôleur MVC renvoie une Image?

puis-je créer un contrôleur qui renvoie simplement une image?

je voudrais Router cette logique à travers un controller, chaque fois qu'une URL telle que la suivante est demandée:

www.mywebsite.com/resource/image/topbanner

Le contrôleur va chercher topbanner.png et envoyez l'image directement au client.

j'ai vu des exemples de cela où vous devez créer une vue - Je ne veux pas utiliser une vue. Je veux tout faire avec, juste la Contrôleur.

est-ce possible?

414
demandé sur Peter Mortensen 2008-10-09 09:58:17

15 réponses

utilisez la méthode 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 assez efficace. J'ai fait un test où j'ai demandé l'image via le contrôleur ( http://localhost/MyController/Image/MyImage ) et l'URL directe ( http://localhost/Images/MyImage.jpg ) et les résultats étaient:

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

Note: il s'agit du délai moyen d'une demande. La moyenne a été calculée en faisant des milliers de requêtes sur la machine locale, de sorte que les totaux ne devraient pas inclure les questions de latence réseau ou de largeur de bande.

492
répondu Brian 2016-07-22 16:17:36

en utilisant la version de sortie 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 évidemment quelques applications spécifiques ici en ce qui concerne la construction du chemin, mais le retour du FileStreamResult est agréable et simple.

j'ai fait quelques tests de performance en ce qui concerne cette action contre votre appel quotidien à l'image (contourner le contrôleur) et la différence entre les moyennes était seulement environ 3 millisecondes (contrôleur moyenne a 68ms, non-contrôleur a été 65ms).

j'avais essayé certaines des autres méthodes mentionnées dans les réponses ici et la performance a été beaucoup plus dramatique... plusieurs des réponses des solutions étaient jusqu'à 6x le non-contrôleur (autres contrôleurs avg 340ms, Non-contrôleur 65ms).

118
répondu Sailing Judo 2014-10-10 23:50:47

À expland sur Dyland de réponse légèrement:

trois classes mettent en œuvre le FileResult classe:

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

Ils sont tous assez explicite:

  • pour les téléchargements de chemin de fichier où le fichier existe sur le disque, utilisez FilePathResult - c'est la façon la plus facile et vous évite d'avoir à utiliser des flux.
  • pour les matrices de bytes [] (s'apparentant à Réponse.BinaryWrite), utilisez FileContentResult .
  • pour les tableaux d'octets[] où vous voulez que le fichier soit téléchargé (content-disposition: attachment), utilisez FileStreamResult de la même manière que ci-dessous, mais avec un MemoryStream et en utilisant GetBuffer() .
  • pour Streams utiliser FileStreamResult . Il est appelé un Filestreamrésult mais il faut un Stream donc je deviner il fonctionne avec un MemoryStream .

ci-dessous est un exemple d'utilisation de la technique contenu-disposition (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;
    }
96
répondu Chris S 2013-06-11 14:19:05

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

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");
}
66
répondu staromeste 2014-08-04 15:45:16

vous pouvez créer votre propre extension et faire de cette façon.

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")%>
10
répondu Sasha Fentsyk 2016-07-07 10:19:32

vous pouvez écrire directement à la réponse, mais alors il n'est pas testable. Il est préférable de renvoyer un résultat D'action dont l'exécution a été différée. Voici mon Résusable StreamResult:

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);
    }
}
8
répondu JarrettV 2008-10-09 20:22:52

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

public FileResult TopBanner() {
  return File("~/Content/images/topbanner.png", "image/png");
}
7
répondu JustinStolle 2011-10-31 23:47:03

mise à JOUR: Il y a de meilleures options que ma réponse originale à cette question. Cela fonctionne assez bien en dehors de MVC, mais il est préférable de s'en tenir aux méthodes intégrées de retour du contenu de l'image. Voir de-voté réponses.

vous pouvez certainement. Essayez ces étapes:

  1. Charger l'image à partir du disque dans un tableau d'octets
  2. cache l'image dans le cas où vous attendez plus de requêtes pour l'image et ne voulez pas le disque I/O (mon exemple n'est pas dans le cache ci-dessous)
  3. modifier le type mime via la réponse.ContentType
  4. Réponse
  5. .BinaryWrite l'image du tableau d'octets

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

Espère que ça aide!

4
répondu Ian Suttle 2011-04-08 21:44:12

regardez ContentResult. Ceci renvoie une chaîne, mais peut être utilisé pour créer votre propre classe BinaryResult-like.

2
répondu leppie 2008-10-09 06:27:57

Solution 1: Rendre une image en 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);
}

alors utilisez-le comme:

@Html.Image(@Model.ImagePath);

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

créer une méthode de contrôleur qui retourne des 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 l'utiliser dans une vue comme:

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

pour utiliser une image rendue à partir de cette action résultant en tout HTML, utiliser

<img src="http://something.com/image/view?id={imageid}>
2
répondu Ajay Kelkar 2015-06-19 18:41:35
if (!System.IO.File.Exists(filePath))
    return SomeHelper.EmptyImageResult(); // preventing JSON GET/POST exception
else
    return new FilePathResult(filePath, contentType);

SomeHelper.EmptyImageResult() doit retourner FileResult avec l'image existante (1x1 transparent, par exemple).

c'est la façon la plus facile si vous avez des fichiers stockés sur le disque local. Si les fichiers sont byte[] ou stream - alors utilisez FileContentResult ou FileStreamResult comme Dylan l'a suggéré.

1
répondu Victor Gelmutdinov 2017-07-05 13:27:56

ça a marché 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 au-dessus de _db.ImageFiles.Find(uuid) est la recherche du dossier d'image dans le db (contexte EF). Il retourne un objet FileImage qui est juste une classe personnalisée que j'ai faite pour le modèle et l'utilise ensuite comme FileContentResult.

public class FileImage {
   public string Uuid { get; set; }
   public byte[] Data { get; set; }
   public string ContentType { get; set; }
}
1
répondu hmojica 2018-04-10 02:09:18

je vois deux options:

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

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

0
répondu Matt Mitchell 2008-10-09 06:26:59

vous pouvez utiliser le HttpContext.Répondre et écrire directement le contenu (WriteFile () pourrait fonctionner pour vous) et ensuite retourner ContentResult de votre action au lieu D'ActionResult.

avertissement: je n'ai pas essayé cela, il est basé sur l'examen des API disponibles. :- )

0
répondu Franci Penov 2008-10-09 06:28:39

vous pouvez utiliser des Fichiers de retour d'un fichier, comme la Vue, le 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>"); }

            }
0
répondu Avinash Urs 2016-03-16 14:23:33