Kann ein ASP.NET MVC-Controller ein Bild zurück?
-
06-07-2019 - |
Frage
Kann ich erstelle einen Controller, der gibt einfach ein Bild Asset?
Ich mag diese Logik über einen Controller zu routen, wann immer eine URL wie die folgenden angefordert:
www.mywebsite.com/resource/image/topbanner
Der Controller topbanner.png
nachschlägt und das Bild senden direkt an den Client zurück.
Ich habe Beispiele gesehen, wo Sie einen Blick zu schaffen haben - ich will nicht eine Ansicht verwenden. Ich will sie alle nur mit dem Controller tun.
Ist das möglich?
Lösung
, um die Basis-Controller Datei-Methode verwenden.
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");
}
Als Hinweis, scheint dies ziemlich effizient. Ich habe einen Test, wo ich das Bild, das durch die Steuerung (http://localhost/MyController/Image/MyImage
) angefordert und durch die direkte URL (http://localhost/Images/MyImage.jpg
) und die Ergebnisse waren:
- MVC: 7,6 Millisekunden pro Foto
- Direct: 6,7 Millisekunden pro Foto
Hinweis: Dies ist die durchschnittliche Zeit eines Ersuchens. Der Mittelwert wurde berechnet, indem Tausende von Anfragen auf der lokalen Maschine zu machen, so dass die Summen nicht die Netzwerk-Latenz oder Bandbreite Probleme sind.
Andere Tipps
die Release-Version von MVC verwenden, hier ist das, was ich tue:
[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");
}
Ich habe offensichtlich einige anwendungsspezifische Sachen in der hier in Bezug auf den Wegebau, aber den Filestreamresult der Rückkehr ist schön und einfach.
Ich habe einige Performance-Tests in Bezug auf diese Aktion gegen den täglichen Anruf zu dem Bild (unter Umgehung der Steuerung) und die Differenz zwischen den Mittelwerten wurde nur etwa 3 Millisekunden (Controller avg war 68ms, nicht-Controller war 65ms).
Ich hatte hier in Antworten erwähnt einige der anderen Methoden ausprobiert und die Performance-Einbußen war viel dramatischer ... einige der Lösungen Antworten so viel wie 6x waren die Nicht-Controller (andere Steuerungen avg 340ms, nicht-Controller 65ms ).
Um auf Dyland Antwort expland leicht:
Drei Klassen implementieren die Fileresult Klasse :
System.Web.Mvc.FileResult
System.Web.Mvc.FileContentResult
System.Web.Mvc.FilePathResult
System.Web.Mvc.FileStreamResult
Sie sind alle ziemlich selbsterklärend:
- Für Dateipfad-Downloads, wo die Datei auf der Festplatte vorhanden ist, verwendet
FilePathResult
- das ist der einfachste Weg ist, und vermeidet man mit Streams verwenden, .
- Für byte [] Arrays (ähnlich Response.BinaryWrite), verwenden
FileContentResult
. - Für byte [] Arrays, wo Sie die Datei herunterladen möchten (Content-Disposition: attachment)., Verwendung
FileStreamResult
in ähnlicher Weise wie unten, aber mit einemMemoryStream
und mitGetBuffer()
- Für
Streams
VerwendungFileStreamResult
. Es ist ein Filestreamresult genannt, aber es dauert eineStream
so würde ich erraten mit einemMemoryStream
funktioniert.
Im Folgenden ist ein Beispiel für die Verwendung der Content-Disposition-Technik (nicht getestet):
[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;
}
Dies kann hilfreich sein, wenn Sie das Bild ändern möchten, bevor es zurückkehrt:
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");
}
Sie können Ihre eigene Erweiterung und machen auf diese Weise erstellen.
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")%>
Sie können direkt auf die Antwort schreiben, aber dann ist es nicht prüfbar. Es wird bevorzugt, eine Actionzurückzugeben, die Ausführung zurückgestellt. Hier ist mein resusable Stream:
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);
}
}
Warum nicht einfach gehen und die Tilde ~
Operator verwenden?
public FileResult TopBanner() {
return File("~/Content/images/topbanner.png", "image/png");
}
UPDATE: Es gibt bessere Optionen als meine ursprüngliche Antwort. Dies funktioniert außerhalb von MVC ganz gut, aber es ist besser, mit den integrierten Methoden Stick Bildinhalt zurück. Siehe oben gestimmt Antworten.
Sie sicherlich können. Probieren Sie die folgenden Schritte:
- Laden das Bild von der Festplatte in einen Byte-Array
- das Bild im Fall cachen Sie mehr Anfragen für das Bild erwarten und wollen nicht auf die Disk-I / O (meine Probe nicht unter nicht-Cache)
- Ändern Sie den MIME-Typ über den Response.ContentType
- Response.BinaryWrite aus dem Bild Byte-Array
Hier ist ein Beispielcode:
string pathToFile = @"C:\Documents and Settings\some_path.jpg";
byte[] imageData = File.ReadAllBytes(pathToFile);
Response.ContentType = "image/jpg";
Response.BinaryWrite(imageData);
Ich hoffe, das hilft!
Lösung 1: Um ein Bild in einer Ansicht von einer Bild-URL zu machen
Sie können Ihre eigene Erweiterungsmethode erstellen
public static MvcHtmlString Image(this HtmlHelper helper,string imageUrl)
{
string tag = "<img src='{0}'/>";
tag = string.Format(tag,imageUrl);
return MvcHtmlString.Create(tag);
}
Dann verwenden Sie es mögen:
@Html.Image(@Model.ImagePath);
Lösung 2: Um das Bild aus der Datenbank zu machen
Erstellen Sie eine Controller-Methode, die Bilddaten wie unten zurück
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);
}
}
Und es in einer Ansicht verwenden wie:
@ { Html.RenderAction("View","Image",new {id=@Model.ImageId})}
ein Bild aus dieser Action in jedem beliebigen HTML gerendert zu verwenden, verwendet
<img src="http://something.com/image/view?id={imageid}>
Das funktionierte für mich. Da ich Bilder auf einer SQL Server-Datenbank zu speichern.
[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;
}
In dem Schnipsel oben _db.ImageFiles.Find(uuid)
wird für die Bilddatei Datensatz in der db (EF Kontext) zu suchen. Es gibt ein FileImage Objekt, das nur eine benutzerdefinierte Klasse, die ich für das Modell gemacht und dann nutzt es als Filecontentresult.
public class FileImage {
public string Uuid { get; set; }
public byte[] Data { get; set; }
public string ContentType { get; set; }
}
Sehen Sie in Content. Dies liefert einen String, kann aber verwendet werden, um Ihre eigene BinaryResult artige Klasse zu machen.
Sie können Datei verwenden, um eine Datei wie Ansicht, Inhalt usw. zurück
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>"); }
}
Ich sehe zwei Möglichkeiten:
1) Implementieren Sie Ihre eigene IViewEngine und stellen Sie die Eigenschaft Viewengine des Controllers Sie Ihre ImageViewEngine verwenden in Ihrem gewünschten „Bild“ -Methode.
2) Verwenden Sie einen Blick :-). Ändern Sie einfach den Inhaltstyp etc.
Sie können die HttpContext.Response verwenden und direkt auf den Inhalt, um es (Writefile () könnte für Sie arbeiten) schreiben und dann wieder aus dem Content Aktion statt Action.
Disclaimer: Ich habe nicht versucht, es basiert auf dem verfügbaren APIs auf der Suche. : -)
if (!System.IO.File.Exists(filePath))
return SomeHelper.EmptyImageResult(); // preventing JSON GET/POST exception
else
return new FilePathResult(filePath, contentType);
SomeHelper.EmptyImageResult()
sollte FileResult
Rückkehr mit vorhandenem Bild (1x1 transparent, zum Beispiel).
Dies ist am einfachsten Weg, wenn Sie Dateien auf dem lokalen Laufwerk gespeichert haben.
Wenn Dateien byte[]
oder stream
sind -. Dann verwenden FileContentResult
oder FileStreamResult
als Dylan vorgeschlagen