Можно ли создать контроллер, который просто возвращает ресурс изображения?
Я хотел бы маршрутизировать эту логику через контроллер, всякий раз, когда запрашивается такой URL-адрес, как:
www.mywebsite.com/resource/image/topbanner
Контроллер будет искать topbanner.png
и отправить это изображение обратно клиенту.
Я видел примеры этого, где вам нужно создать представление - я не хочу использовать представление. Я хочу сделать это всего лишь с контроллером.
Возможно ли это?
Используйте базовый контроллер Файловый метод.
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, вот что я делаю:
[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 раз без контроллера (другие контроллеры avg 340ms, неконтроллерные 65 мс).
Path.Combine
вместо Path.Combine
для более безопасного и удобочитаемого кода.
Чтобы немного объяснить ответ Диланда:
Три класса реализуют класс 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;
}
Это может быть полезно, если вы хотите изменить изображение перед его возвратом:
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")%>
Я задал аналогичный вопрос здесь Создание частной фотогалереи с использованием Asp.Net MVC и в итоге нашел отличное руководство для этого.
После этого руководства я создал класс ImageResult. https://blog.maartenballiauw.be/post/2008/05/13/aspnet-mvc-custom-actionresult.html
Вы можете напрямую написать ответ, но тогда это невозможно проверить. Предпочтительно возвращать 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);
}
}
Почему бы не пойти простым и использовать оператор тильды ~
?
public FileResult TopBanner() {
return File("~/Content/images/topbanner.png", "image/png");
}
ОБНОВЛЕНИЕ: Есть лучшие варианты, чем мой первоначальный ответ. Это работает вне MVC довольно хорошо, но лучше придерживаться встроенных методов возврата содержимого изображения. См. Ответы на голосование.
Вы, конечно, можете. Выполните следующие действия:
Вот пример кода:
string pathToFile = @"C:\Documents and Settings\some_path.jpg";
byte[] imageData = File.ReadAllBytes(pathToFile);
Response.ContentType = "image/jpg";
Response.BinaryWrite(imageData);
Надеюсь, что это поможет!
Решение 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}>
Посмотрите на ContentResult. Это возвращает строку, но может использоваться для создания собственного класса BinaryResult.
Это сработало для меня. Поскольку я храню изображения в базе данных 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)
выполняется поиск записи файла изображения в db (контекст EF). Он возвращает объект FileImage, который является обычным классом, который я создал для модели, а затем использует его как FileContentResult.
public class FileImage {
public string Uuid { get; set; }
public byte[] Data { get; set; }
public string ContentType { get; set; }
}
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
, как предложил Дилан.
вы можете использовать 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>"); }
}
Вы можете использовать HttpContext.Response и напрямую записывать содержимое на него (WriteFile() может сработать для вас), а затем вернуть ContentResult из вашего действия вместо ActionResult.
Отказ от ответственности: я не пробовал этого, это основано на просмотре доступных API.: -)
Я вижу два варианта:
1) Внедрите свой собственный IViewEngine и установите свойство ViewEngine Контроллера, который вы используете для вашего ImageViewEngine, в желаемом "образном" методе.
2) Используйте представление:-). Просто измените тип контента и т.д.