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

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

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

www.mywebsite.com/resource/image/topbanner

Контроллер будет искать topbanner.png и отправить это изображение обратно клиенту.

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

Возможно ли это?

Ответ 1

Используйте базовый контроллер Файловый метод.

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 миллисекунды за фотографию
  • Direct: 6.7 миллисекунды за фотографию

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

Ответ 2

Используя версию выпуска 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 мс).

Ответ 3

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

Три класса реализуют класс 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;
    }

Ответ 4

Это может быть полезно, если вы хотите изменить изображение перед его возвратом:

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

Ответ 5

Вы можете создать собственное расширение и сделать это.

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")%>

Ответ 6

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

Ответ 8

Почему бы не пойти простым и использовать оператор тильды ~?

public FileResult TopBanner() {
  return File("~/Content/images/topbanner.png", "image/png");
}

Ответ 9

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

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

  • Загрузите изображение с диска в массив байтов
  • кэшируйте изображение в случае, если вы ожидаете большего количества запросов для изображения и не хотите ввода/вывода на диске (мой пример не кэширует его ниже)
  • Измените тип mime с помощью Response.ContentType
  • Response.BinaryЗарегистрируйте массив байтов изображений

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

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

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

Ответ 10

Решение 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}>

Ответ 11

Это сработало для меня. Поскольку я храню изображения в базе данных 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; }
}

Ответ 12

вы можете использовать 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>"); }

            }

Ответ 13

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

Ответ 14

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

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

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

Ответ 15

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

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

Ответ 16

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, как предложил Дилан.

Ответ 17

Я также столкнулся с аналогичным требованием,

Поэтому в моем случае я делаю запрос в Controller с путем к папке с изображениями, который в свою очередь отправляет обратно объект 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();
}

Ответ 18

Ниже код использует 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();
    result.Save(ms, ImageFormat.Jpeg);
    ms.Position = 0;
    return new FileStreamResult(ms, "image/jpeg");
}