Может ли контроллер ASP.NET MVC вернуть изображение?

Могу ли я создать контроллер, который просто возвращает ресурс изображения?

Я хотел бы направить эту логику через контроллер всякий раз, когда запрашивается URL-адрес, подобный следующему:

www.mywebsite.com/resource/image/topbanner

Контроллер найдет topbanner.png и отправит это изображение напрямую клиенту.

Я видел примеры этого, когда вам нужно создать представление - я не хочу использовать представление. Я хочу делать все это только с помощью контроллера.

Это возможно?

Если вы хотите изменить изображение, используйте ImageResizing.Net HttpModule для лучшей производительности. Если вы этого не сделаете, FilePathResult добавляет лишь несколько процентов накладных расходов. Перезапись URL добавляет немного меньше.

Lilith River 16.10.2011 02:53

Почему бы не использовать Web Api Controller вместо MVC? ApiController class

A-Sharabiani 02.06.2016 20:40

Я задал аналогичный вопрос здесь 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

Vyrotek 22.10.2008 04:06
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
464
3
244 383
19
Перейти к ответу Данный вопрос помечен как решенный

Ответы 19

Я вижу два варианта:

1) Внедрите свой собственный IViewEngine и установите свойство ViewEngine контроллера, который вы используете, для вашего ImageViewEngine в желаемом методе «изображения».

2) Используйте вид :-). Просто измените тип контента и т. д.

Это может быть проблемой из-за лишних пробелов или CRLF в представлении.

Elan Hasson 12.09.2011 07:48

Я ошибся в своем последнем посте ... msdn.microsoft.com/en-us/library/… Вы можете использовать класс WebImage и WebImage.Write в представлении :)

Elan Hasson 12.09.2011 08:51

Посмотрите ContentResult. Это возвращает строку, но может использоваться для создания вашего собственного класса, подобного BinaryResult.

Вы можете использовать HttpContext.Response и напрямую записывать в него контент (WriteFile () может работать для вас), а затем возвращать ContentResult из вашего действия вместо ActionResult.

Отказ от ответственности: я не пробовал это, это основано на просмотре доступных API. :-)

Да, я только что заметил, что ContentResult поддерживает только строки, но достаточно легко создать свой собственный класс на основе ActionResult.

leppie 09.10.2008 10:31

ОБНОВЛЕНИЕ: есть варианты получше, чем мой первоначальный ответ. Это довольно хорошо работает вне MVC, но лучше придерживаться встроенных методов возврата содержимого изображения. См. Ответы с одобренными голосами.

Вы, конечно, можете. Попробуйте выполнить следующие действия:

  1. Загрузите изображение с диска в массив байтов
  2. кэшировать изображение в случае, если вы ожидаете большего количества запросов на изображение и не хотите дискового ввода-вывода (мой образец не кэширует его ниже)
  3. Измените тип пантомимы через Response.ContentType.
  4. Response.Binary Запишите байтовый массив изображения.

Вот пример кода:

string pathToFile = @"C:\Documents and Settings\some_path.jpg";
byte[] imageData = File.ReadAllBytes(pathToFile);
Response.ContentType = "image/jpg";
Response.BinaryWrite(imageData);

Надеюсь, это поможет!

и как это будет выглядеть в действии контроллера?

CRice 08.04.2011 03:25

Вы можете написать прямо в ответ, но тогда его нельзя будет проверить. Предпочтительно возвращать 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, если изображение не изменилось с момента последнего запроса.

dariol 03.10.2010 19:16

Вы можете использовать Path.Combine вместо concat для более безопасного и читаемого кода.

Marcell Toth 07.11.2018 20:32
Ответ принят как подходящий

Используйте метод 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), и результаты были такими:

  • MVC: 7,6 миллисекунд на фото
  • Прямой: 6,7 миллисекунд на фото

Примечание: это среднее время запроса. Среднее значение было рассчитано путем выполнения тысяч запросов на локальном компьютере, поэтому итоговые значения не должны включать в себя задержку в сети или проблемы с пропускной способностью.

Для тех, кто сейчас задается этим вопросом, это решение, которое лучше всего сработало для меня.

Clarence Klopfstein 25.12.2009 08:09

Отличный ответ, Брайан. Во-первых, параметр contentType в вызове метода File () в вашем примере должен быть «image / jpeg». В противном случае IE8 выдает диалоговое окно загрузки. И этот ответ следует выбрать.

Jason Slocomb 26.05.2010 07:33

Это небезопасный код. Разрешение пользователю передавать имя файла (путь) таким образом означает, что он потенциально может получить доступ к файлам из любого места на сервере. Можно предупредить людей, чтобы они не использовали его как есть.

Ian Mercer 28.01.2011 10:53

Этот ответ не имеет смысла. Если вы работаете с файлами, которые сохранены на сервере в какой-либо папке в корне вашего веб-приложения, лучше и быстрее просто использовать помощники View и Html или Url MVC для построения пути к изображениям для тега <img> или создайте собственных помощников для полного рендеринга тега <img>. Единственный раз, когда имеет смысл использовать FileResult, - это когда у вас есть файлы, изображения или любой другой вид содержимого byte [], сохраненный в хранилище данных.

mare 26.06.2011 13:24

Если только вы не создаете файлы на лету по мере необходимости и не кешируете их после создания (это то, что мы делаем).

Brian 27.06.2011 05:45

Я использовал это, чтобы сделать то же самое с PDF. +1 Спасибо

Matt 13.10.2011 17:16

@ mare - вы также можете сделать это, если вы обслуживаете файлы из ограниченного места, например у вас могут быть изображения в App_Data, которые должны быть подписаны одними пользователями вашего приложения, но не другими. Использование действия контроллера для их обслуживания позволяет ограничить доступ.

Russ Cam 19.10.2011 01:12

Я не уверен, но быстрый тест этого решения показал, что оно игнорирует заголовки «If-Modified-Since» и всегда возвращает файл. В некоторых ситуациях это может быть желательно, но это приведет к обходу кэширования на стороне клиента и ухудшит производительность.

RonnBlack 08.06.2012 12:25

Как уже упоминалось, будьте осторожны при построении пути, поскольку я видел фактический производственный код, который позволял пользователю перемещаться вверх по каталогу с тщательно созданной строкой запроса POST или запроса: /../../../danger/someFileTheyTHoughtWasInaccessible

AaronLS 30.04.2014 03:18

Чтобы немного пояснить ответ Дайланда:

Три класса реализуют класс FileResult:

System.Web.Mvc.FileResult
      System.Web.Mvc.FileContentResult
      System.Web.Mvc.FilePathResult
      System.Web.Mvc.FileStreamResult

Все они говорят сами за себя:

  • Для загрузки по пути к файлу, когда файл существует на диске, используйте FilePathResult - это самый простой способ, позволяющий избежать использования потоков.
  • Для массивов byte [] (похожих на Response.BinaryWrite) используйте FileContentResult.
  • Для массивов byte [], в которые вы хотите загрузить файл (content-disposition: attachment), используйте 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;
    }

Часть этого сообщения, посвященная размещению контента, была чрезвычайно полезной.

Diego 28.10.2010 23:44

VS сообщает мне, что эта перегрузка FileStream () устарела.

MrBoJangles 24.01.2012 02:36

Обратите внимание: если в имени файла есть запятая, Chrome отклонит его с ошибкой «получено слишком много заголовков». Поэтому замените все запятые на «-» или «».

Chris S 19.04.2012 02:30

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");
}

Спасибо. Это идеально подходит для сценария, когда прокси-сервер необходим для загрузки изображения, требующего аутентификации, которая не может быть выполнена на стороне клиента.

Hong 24.03.2012 18:19

Вы забываете избавиться от колоссальных трех собственных объектов: Font, SolidBrush и Image.

Wout 25.09.2012 14:07

Предлагаемое улучшение здесь: вы создаете поток памяти, записываете данные, а затем создаете результат File с данными, используя .ToArray (). Вы также можете просто вызвать ms.Seek (0, SeekOrigin.Begin), а затем вернуть File (ms, " image / png ") // возвращаем сам поток

Quango 13.12.2012 13:39

Вы можете создать собственное расширение и поступить таким образом.

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

Goodies 19.07.2020 03:52

Прочтите изображение, преобразуйте его в 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");
}

(Не забудьте отметить это как ответ)

Другие вопросы по теме