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?
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.
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).
À 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 unMemoryStream
et en utilisantGetBuffer()
. - pour
Streams
utiliserFileStreamResult
. Il est appelé un Filestreamrésult mais il faut unStream
donc je deviner il fonctionne avec unMemoryStream
.
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;
}
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");
}
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")%>
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);
}
}
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 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:
- Charger l'image à partir du disque dans un tableau d'octets
- 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)
- modifier le type mime via la réponse.ContentType Réponse
- .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!
regardez ContentResult. Ceci renvoie une chaîne, mais peut être utilisé pour créer votre propre classe BinaryResult-like.
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}>
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é.
ç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; }
}
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.
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. :- )
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>"); }
}