Могу ли я создать контроллер, который просто возвращает ресурс изображения?
Я хотел бы направить эту логику через контроллер всякий раз, когда запрашивается URL-адрес, подобный следующему:
www.mywebsite.com/resource/image/topbanner
Контроллер найдет topbanner.png и отправит это изображение напрямую клиенту.
Я видел примеры этого, когда вам нужно создать представление - я не хочу использовать представление. Я хочу делать все это только с помощью контроллера.
Это возможно?
Почему бы не использовать Web Api Controller вместо MVC? ApiController class
Я задал аналогичный вопрос здесь https://stackoverflow.com/questions/155906/creating-a-privat электронная фотогалерея-usin g-aspnet-mvc и в итоге нашел отличное руководство для этого. Следуя этому руководству, я создал класс ImageResult. https://blog.maartenballiauw.be/post/2008/05/13/aspnet-mvc-c ustom-actionresult.h tml





Я вижу два варианта:
1) Внедрите свой собственный IViewEngine и установите свойство ViewEngine контроллера, который вы используете, для вашего ImageViewEngine в желаемом методе «изображения».
2) Используйте вид :-). Просто измените тип контента и т. д.
Это может быть проблемой из-за лишних пробелов или CRLF в представлении.
Я ошибся в своем последнем посте ... msdn.microsoft.com/en-us/library/… Вы можете использовать класс WebImage и WebImage.Write в представлении :)
Посмотрите ContentResult. Это возвращает строку, но может использоваться для создания вашего собственного класса, подобного BinaryResult.
Вы можете использовать HttpContext.Response и напрямую записывать в него контент (WriteFile () может работать для вас), а затем возвращать ContentResult из вашего действия вместо ActionResult.
Отказ от ответственности: я не пробовал это, это основано на просмотре доступных API. :-)
Да, я только что заметил, что ContentResult поддерживает только строки, но достаточно легко создать свой собственный класс на основе ActionResult.
ОБНОВЛЕНИЕ: есть варианты получше, чем мой первоначальный ответ. Это довольно хорошо работает вне MVC, но лучше придерживаться встроенных методов возврата содержимого изображения. См. Ответы с одобренными голосами.
Вы, конечно, можете. Попробуйте выполнить следующие действия:
Вот пример кода:
string pathToFile = @"C:\Documents and Settings\some_path.jpg";
byte[] imageData = File.ReadAllBytes(pathToFile);
Response.ContentType = "image/jpg";
Response.BinaryWrite(imageData);
Надеюсь, это поможет!
и как это будет выглядеть в действии контроллера?
Вы можете написать прямо в ответ, но тогда его нельзя будет проверить. Предпочтительно возвращать ActionResult с отложенным выполнением. Вот мой многоразовый 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);
}
}
Используя релизную версию 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 миллисекунд (среднее значение контроллера - 68 мс, неконтроллер - 65 мс).
Я пробовал некоторые другие методы, упомянутые в ответах здесь, и падение производительности было гораздо более драматичным ... ответы на некоторые решения были в 6 раз больше, чем неконтроллер (другие контроллеры в среднем 340 мс, неконтроллер 65 мс).
Что насчет изображения не модифицируется? FileStreamResult должен отправить 304, если изображение не изменилось с момента последнего запроса.
Вы можете использовать Path.Combine вместо concat для более безопасного и читаемого кода.
Используйте метод File базовых контроллеров.
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), и результаты были такими:
Примечание: это среднее время запроса. Среднее значение было рассчитано путем выполнения тысяч запросов на локальном компьютере, поэтому итоговые значения не должны включать в себя задержку в сети или проблемы с пропускной способностью.
Для тех, кто сейчас задается этим вопросом, это решение, которое лучше всего сработало для меня.
Отличный ответ, Брайан. Во-первых, параметр contentType в вызове метода File () в вашем примере должен быть «image / jpeg». В противном случае IE8 выдает диалоговое окно загрузки. И этот ответ следует выбрать.
Это небезопасный код. Разрешение пользователю передавать имя файла (путь) таким образом означает, что он потенциально может получить доступ к файлам из любого места на сервере. Можно предупредить людей, чтобы они не использовали его как есть.
Этот ответ не имеет смысла. Если вы работаете с файлами, которые сохранены на сервере в какой-либо папке в корне вашего веб-приложения, лучше и быстрее просто использовать помощники View и Html или Url MVC для построения пути к изображениям для тега <img> или создайте собственных помощников для полного рендеринга тега <img>. Единственный раз, когда имеет смысл использовать FileResult, - это когда у вас есть файлы, изображения или любой другой вид содержимого byte [], сохраненный в хранилище данных.
Если только вы не создаете файлы на лету по мере необходимости и не кешируете их после создания (это то, что мы делаем).
Я использовал это, чтобы сделать то же самое с PDF. +1 Спасибо
@ mare - вы также можете сделать это, если вы обслуживаете файлы из ограниченного места, например у вас могут быть изображения в App_Data, которые должны быть подписаны одними пользователями вашего приложения, но не другими. Использование действия контроллера для их обслуживания позволяет ограничить доступ.
Я не уверен, но быстрый тест этого решения показал, что оно игнорирует заголовки «If-Modified-Since» и всегда возвращает файл. В некоторых ситуациях это может быть желательно, но это приведет к обходу кэширования на стороне клиента и ухудшит производительность.
Как уже упоминалось, будьте осторожны при построении пути, поскольку я видел фактический производственный код, который позволял пользователю перемещаться вверх по каталогу с тщательно созданной строкой запроса POST или запроса: /../../../danger/someFileTheyTHoughtWasInaccessible
Чтобы немного пояснить ответ Дайланда:
Три класса реализуют класс FileResult:
System.Web.Mvc.FileResult
System.Web.Mvc.FileContentResult
System.Web.Mvc.FilePathResult
System.Web.Mvc.FileStreamResult
Все они говорят сами за себя:
FilePathResult - это самый простой способ, позволяющий избежать использования потоков.FileContentResult.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;
}
Часть этого сообщения, посвященная размещению контента, была чрезвычайно полезной.
VS сообщает мне, что эта перегрузка FileStream () устарела.
Обратите внимание: если в имени файла есть запятая, Chrome отклонит его с ошибкой «получено слишком много заголовков». Поэтому замените все запятые на «-» или «».
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, как предложил Дилан.
Это может быть полезно, если вы хотите изменить изображение перед его возвратом:
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");
}
Спасибо. Это идеально подходит для сценария, когда прокси-сервер необходим для загрузки изображения, требующего аутентификации, которая не может быть выполнена на стороне клиента.
Вы забываете избавиться от колоссальных трех собственных объектов: Font, SolidBrush и Image.
Предлагаемое улучшение здесь: вы создаете поток памяти, записываете данные, а затем создаете результат File с данными, используя .ToArray (). Вы также можете просто вызвать ms.Seek (0, SeekOrigin.Begin), а затем вернуть File (ms, " 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")%>
Почему бы не пойти по простому и не использовать оператор тильды ~?
public FileResult TopBanner() {
return File("~/Content/images/topbanner.png", "image/png");
}
Решение 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]})}
Чтобы использовать изображение, отображаемое в результате этого действия, в любом HTML, используйте
<img src = "http://something.com/image/view?id = {imageid}>
вы можете использовать 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>"); }
}
Это сработало для меня. Поскольку я храню изображения в базе данных 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; }
}
Я также столкнулся с аналогичным требованием,
Итак, в моем случае я делаю запрос к контроллеру с путем к папке изображения, который, в свою очередь, отправляет обратно объект ImageResult.
Следующий фрагмент кода иллюстрирует работу:
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>";
И в контроллере из пути к изображению я создаю изображение и возвращаю его вызывающему
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();
}
В приведенном ниже коде для загрузки изображения используется System.Drawing.Bitmap.
using System.Drawing;
using System.Drawing.Imaging;
public IActionResult Get()
{
string filename = "Image/test.jpg";
var bitmap = new Bitmap(filename);
var ms = new System.IO.MemoryStream();
bitmap.Save(ms, ImageFormat.Jpeg);
ms.Position = 0;
return new FileStreamResult(ms, "image/jpeg");
}
Протестировано, но исправлена небольшая ошибка в этом примере кода. Используется Edit .. "result" должен быть "bitmap" в операторе Save. Действительно полезный пример, спасибо! +1
Прочтите изображение, преобразуйте его в byte[], затем верните File() с типом содержимого.
public ActionResult ImageResult(Image image, ImageFormat format, string contentType) {
using (var stream = new MemoryStream())
{
image.Save(stream, format);
return File(stream.ToArray(), contentType);
}
}
}
Вот способы использования:
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using Microsoft.AspNetCore.Mvc;
Да, вы можете вернуть изображение
public ActionResult GetImage(string imageFileName)
{
var path = Path.Combine(Server.MapPath("/Images"), imageFileName + ".jpg");
return base.File(path, "image/jpeg");
}
(Не забудьте отметить это как ответ)
Если вы хотите изменить изображение, используйте ImageResizing.Net HttpModule для лучшей производительности. Если вы этого не сделаете, FilePathResult добавляет лишь несколько процентов накладных расходов. Перезапись URL добавляет немного меньше.