Puis-je créer un contrôleur qui renvoie simplement un fichier image?
J'aimerais acheminer cette logique via un contrôleur, chaque fois qu'une 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 veux pas utiliser une vue. Je veux tout faire avec seulement le contrôleur.
Est-ce possible?
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");
}
A noter que 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 via l'URL directe (http://localhost/Images/MyImage.jpg
) et les résultats sont les suivants:
Remarque: il s’agit du temps moyen d’une demande. La moyenne a été calculée en effectuant des milliers de demandes sur la machine locale. Par conséquent, les totaux ne doivent pas inclure les problèmes de latence du réseau ou de bande passante.
En utilisant la version release 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");
}
Il est évident que j'ai quelques éléments spécifiques à l'application ici concernant la construction du chemin, mais le retour de FileStreamResult est agréable et simple.
J'ai effectué des tests de performances 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 avg du contrôleur était de 68 ms, celle des non-contrôleurs était de 65 ms).
J'avais déjà essayé certaines des méthodes mentionnées dans les réponses ci-dessus et les performances étaient bien plus dramatiques ... plusieurs des solutions proposées étaient autant que 6x 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:
FilePathResult
- c'est le moyen le plus simple et vous évite d'avoir à utiliser Streams.FileContentResult
.FileStreamResult
de la même manière que ci-dessous, mais avec un MemoryStream
et en utilisant GetBuffer()
.Streams
, utilisez FileStreamResult
. Cela s'appelle un FileStreamResult mais il faut un Stream
donc je devrais deviner cela fonctionne avec un MemoryStream
.Vous trouverez ci-dessous 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 le 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")%>
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");
}
MISE À JOUR: 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 qui ont été votées.
Vous pouvez certainement. Essayez ces étapes:
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 t'as aidé!
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 ci-dessus, _db.ImageFiles.Find(uuid)
recherche l'enregistrement du fichier image dans la base de données (contexte EF). Il retourne 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; }
}
Solution 1: 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);
}
Alors utilisez-le comme:
@Html.Image(@Model.ImagePath);
Solution 2: Rendre 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 comme celle-ci:
@ { Html.RenderAction("View","Image",new {[email protected]})}
Pour utiliser une image rendue à partir de cette action dans un code HTML quelconque, utilisez
<img src="http://something.com/image/view?id={imageid}>
Regardez ContentResult. Ceci retourne une chaîne, mais peut être utilisé pour créer votre propre classe similaire à BinaryResult.
vous pouvez utiliser File pour retourner un fichier comme View, Content 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 la méthode "image" de votre choix.
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 à partir de votre action au lieu d'ActionResult.
Disclaimer: 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).
C'est le moyen le plus simple si vous avez des fichiers stockés sur le lecteur local. Si les fichiers sont byte[]
ou stream
- utilisez alors FileContentResult
ou FileStreamResult
comme suggéré par Dylan.
J'ai aussi rencontré une exigence similaire,
Donc, dans mon cas, je fais une demande au contrôleur avec le chemin du dossier de l'image, qui en retour renvoie un objet ImageResult.
L'extrait de code suivant illustre le travail:
var src = string.Format("/GenericGrid.mvc/DocumentPreviewImageLink?fullpath={0}&routingId={1}&siteCode={2}", fullFilePath, metaInfo.RoutingId, da.SiteCode);
if (enlarged)
result = "<a class='thumbnail' href='#thumb'>" +
"<img src='" + src + "' height='66px' border='0' />" +
"<span><img src='" + src + "' /></span>" +
"</a>";
else
result = "<span><img src='" + src + "' height='150px' border='0' /></span>";
Et dans le contrôleur à partir du chemin de l'image, je produis l'image et la renvoie à l'appelant.
try
{
var file = new FileInfo(fullpath);
if (!file.Exists)
return string.Empty;
var image = new WebImage(fullpath);
return new ImageResult(new MemoryStream(image.GetBytes()), "image/jpg");
}
catch(Exception ex)
{
return "File Error : "+ex.ToString();
}