Может Ан ASP.NET контроллер MVC возвращает изображение?
Можно ли создать контроллер, который просто возвращает ресурс изображения?
Я хотел бы направить эту логику через контроллер, когда запрашивается URL-адрес, такой как:
www.mywebsite.com/resource/image/topbanner
Контроллер будет искать topbanner.png и отправлять это изображение обратно клиенту.
Я видел примеры этого, когда вы должны создать представление - я не хочу использовать представление. Я хочу сделать все это только с помощью контроллера.
Возможно ли это?
15 ответов:
Используйте метод файла базовых контроллеров.
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"); }В качестве примечания, это кажется довольно эффективным. Я сделал тест, где я запросил Изображение через контроллер (
http://localhost/MyController/Image/MyImage) и через прямой URL (http://localhost/Images/MyImage.jpg), и результаты были:Примечание: это среднее время выполнения запроса. Среднее значение было рассчитано, сделав тысячи запросов на местном таким образом, итоговые данные не должны включать задержки в сети или проблемы с пропускной способностью.
- MVC: 7,6 миллисекунд на фотографию
- прямая: 6,7 миллисекунд на фотографию
Используя релизную версию MVC, вот что я делаю:
[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"); }Очевидно, что у меня есть некоторые специфические для приложения вещи, касающиеся построения пути, но возвращение FileStreamResult приятно и просто.
Я провел некоторое тестирование производительности в отношении этого действия против вашего повседневного вызова к изображению (минуя контроллер), и разница между средними значениями составила всего около 3 миллисекунд (контроллер avg был 68 МС, не контроллер-65 мс).
Я попробовал некоторые другие методы, упомянутые в ответах здесь, и хит производительности был гораздо более драматичным... некоторые из ответов решений были аж в 6 раз больше неконтролируемых (другие контроллеры avg 340ms, неконтролируемые 65ms).
Чтобы немного объяснить ответ Дайланда:
Три класса реализуют класс FileResult :
System.Web.Mvc.FileResult System.Web.Mvc.FileContentResult System.Web.Mvc.FilePathResult System.Web.Mvc.FileStreamResultВсе они довольно понятны:
- для загрузки пути к файлу, где файл существует на диске, используйте
FilePathResult- это самый простой способ и позволяет избежать необходимости использовать потоки.- для массивов байт [] (сродни ответу.BinaryWrite), используйте
FileContentResult.- для массивов byte [], в которые вы хотите загрузить файл (content-disposition: приложение), использовать
FileStreamResultаналогично приведенному ниже, но с помощьюMemoryStreamи используяGetBuffer().- для
StreamsИспользуйтеFileStreamResult. Это называется FileStreamResult, но он принимаетStream, поэтому я бы предположил, что он работает сMemoryStream.Ниже приведен пример использования метода "контент-диспозиция" (не тестировался):
[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; }
Это может быть полезно, если вы хотите изменить изображение перед его возвратом:
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"); }
Вы можете создать свое собственное расширение и сделать это таким образом.
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")%>
Вы можете написать непосредственно в ответ,но тогда он не будет проверяться. Предпочтительно вернуть ActionResult, который отложил выполнение. Вот мой resusable 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); } }
Почему бы не пойти просто и не использовать оператор tilde
~?public FileResult TopBanner() { return File("~/Content/images/topbanner.png", "image/png"); }
Обновление: есть лучшие варианты, чем мой первоначальный ответ. Это работает за пределами MVC довольно хорошо, но лучше придерживаться встроенных методов возврата содержимого изображения. Смотрите вверх-проголосовали ответы.
Вы, конечно, можете. Попробуйте выполнить следующие действия:
- загрузите образ с диска в массив байтов
- кэширование образа в случае, если вы ожидаете больше запросов для образа и не хотите дисковый ввод-вывод (мой пример не кэширует его ниже)
- изменение типа mime через ответ.ContentType
- ответ.BinaryWrite из массива байтов изображения
Вот пример кода:
string pathToFile = @"C:\Documents and Settings\some_path.jpg"; byte[] imageData = File.ReadAllBytes(pathToFile); Response.ContentType = "image/jpg"; Response.BinaryWrite(imageData);Надеюсь, это поможет!
Посмотрите на ContentResult. Это возвращает строку,но может использоваться для создания собственного класса BinaryResult-like.
Решение 1: визуализация изображения в виде из URL изображения
Вы можете создать свой собственный метод расширения:
public static MvcHtmlString Image(this HtmlHelper helper,string imageUrl) { string tag = "<img src='{0}'/>"; tag = string.Format(tag,imageUrl); return MvcHtmlString.Create(tag); }Затем используйте его как:
@Html.Image(@Model.ImagePath);Решение 2: визуализация изображения из базы данных
Создайте метод контроллера, который возвращает данные изображения, как показано ниже
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); } }И использовать его в представлении типа:
@ { Html.RenderAction("View","Image",new {[email protected]})}Чтобы использовать изображение, полученное из этого actionresult в любом HTML, используйте
<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()должен возвращатьFileResultс существующим изображением (1x1 прозрачный, например).Это самый простой способ, если у вас есть файлы, хранящиеся на локальном диске. Если файлы
byte[]илиstream- тогда используйтеFileContentResultилиFileStreamResult, как предложил Дилан.
Это сработало для меня. Так как я храню изображения в базе данных 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; }В приведенном выше фрагменте
_db.ImageFiles.Find(uuid)ищет запись файла изображения в БД (контекст EF). Он возвращает объект FileImage, который является всего лишь пользовательским классом, созданным для модели, а затем использует его как FileContentResult.public class FileImage { public string Uuid { get; set; } public byte[] Data { get; set; } public string ContentType { get; set; } }
Я вижу два варианта:
1) реализуйте свой собственный IViewEngine и установите свойство ViewEngine контроллера, который вы используете, в свой ImageViewEngine в желаемом методе "image".
2) Используйте вид :-). Просто измените тип контента и т.д.
Можно использовать HttpContext.Ответ и непосредственно записать содержимое в него (WriteFile () может работать для вас), а затем вернуть ContentResult из вашего действия вместо ActionResult.
Отказ от ответственности: я не пробовал это, это основано на просмотре доступных API. :- )
Вы можете использовать File для возврата таких файлов, как View, Content и т. д.
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>"); } }
Comments