Pull to refresh

Вывод картинок в ASP .NET MVC

Reading time6 min
Views11K
Технологии: .NET Framework 3.5, ASP .NET MVC
Языки: Visual Basic .NET, C#

Довольно часто при разработке веб-приложений требуется выдать пользователю данные в отличном от html формате. Например, вывести уменьшенную копию изображения (ThumbImage), или же выдать защищенные данные из БД. В ASP .NET WebForms для этих целей можно использовать Handlers (хэндлеры), либо обычные ASPX-страницы. В ASP .NET MVC ситуация немного изменилась. Хотя конечно, по прежнему, ничто не запрещается использовать хэндлеры. Разве что, может потребоваться правило для Routing, типа: routes.IgnoreRoute("{resource}.ashx").
В данном обзоре будет рассмотрен пример вывода изображения средствами ASP .NET MVC, однако описанный подход можно использовать для вывода данных абсолютно любого формата.


ActionResult



Как известно (надеюсь, что известно), результатом выполнения любого действия (action) контроллера (controller) является ActionResult. ActionResult – это базовый класс, от которого наследуются все классы, в конечном итоге, формирующие контент для вывода клиенту. Например: ViewResult, JsonResult, ContentResult, RedirectResult и т.д. Сам по себе, класс ActionResult использовать нельзя, от него можно только наследоваться, и за счет этого, список результатов выполнения действий контроллеров ограничивается лишь фантазией разработчиков.
Формирование данных для вывода происходит в методе ExecuteResult.

ImageResult



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

VB .NET:
Public Class ImageResult
  Inherits ActionResult
End Class


C#:
public class ImageResult: ActionResult
{ }


Как я уже говорил, контент формируется в методе ExecuteResult, однако, прежде чем это делать, необходимо получить исходный материал. Другими словами, передать в класс информацию, на основе которой будут формироваться конечные данные. В случае с изображениями, наиболее универсально будет передавать в класс Stream. Также потребуется передать информацию о типе контента (Content-Type), чтобы поисковики не пугались, и браузеры не гадали на кофейной гуще и, как следствие, не травмировали психику пользователя. Для этого придется написать пару соответствующих свойств, а для полноты картины прописать конструктор.

VB .NET:
Private _ImageStream As Stream
Private _ContentType As String = ""

Public Property ImageStream() As Stream
  Get
    Return _ImageStream
  End Get
  Set(ByVal value As Stream)
    _ImageStream = value
  End Set
End Property

Public Property ContentType() As String
  Get
    Return _ContentType
  End Get
  Set(ByVal value As String)
    _ContentType = value
  End Set
End Property

Public Sub New(ByVal imageStream As Stream, ByVal contentType As String)
  If imageStream Is Nothing Then Throw New ArgumentNullException("imageStream")
  If String.IsNullOrEmpty(contentType) Then Throw New ArgumentNullException("contentType")
  _ImageStream = imageStream
  _ContentType = contentType
End Sub


C#:
private static Stream _ImageStream;
private static string _ContentType = "";

public static Stream ImageStream
{
  get { return _ImageStream; }
  set { _ImageStream = value; }
}

public static string ContentType
{
  get { return _ContentType; }
  set { _ContentType = value; }
}

public ImageResult(Stream imageStream, string contentType)
{
  if (imageStream == null) throw new ArgumentNullException("imageStream");
  if (String.IsNullOrEmpty(contentType)) throw new ArgumentNullException("contentType");
  _ImageStream = imageStream;
  _ContentType = contentType;
}


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

VB .NET:
Public Overrides Sub ExecuteResult(ByVal context As System.Web.Mvc.ControllerContext)
  If context Is Nothing Then Throw New ArgumentNullException("context")
  Dim Response As HttpResponseBase = context.HttpContext.Response
  Response.ContentType = _ContentType
  Dim buffer(1024) As Byte
  Do
    Dim read As Integer = _ImageStream.Read(buffer, 0, buffer.Length)
    If read = 0 Then Exit Do
    Response.OutputStream.Write(buffer, 0, read)
  Loop
  Response.End()
End Sub


C#:
public static override void ExecuteResult(System.Web.Mvc.ControllerContext context)
{
  if (context == null) throw new ArgumentNullException("context");
  HttpResponseBase Response = context.HttpContext().Response;
  Response.ContentType = _ContentType;
  byte[] buffer = new byte[1024];
  do
  {
    int read = _ImageStream.Read(buffer, 0, buffer.Length);
    if (read == 0) { break; }
    Response.OutputStream.Write(buffer, 0, read);
  } while (true);
  Response.End();
}


Теперь результатом действия любого контроллера может быть ImageResult.

VB .NET:
Return New ImageResult(System.IO.File.OpenRead("C:\kbyte.ru.gif"), "image/gif")

C#:
return new ImageResult(System.IO.File.OpenRead("C:\\kbyte.ru.gif"), "image/gif");

Extension



Для полного счастья, можно написать расширение для System.Web.Mvc.Controller. Для этого, вбшники могут создать обычный модуль (Module), а сишники static-класс, с именем, например ControllerExtensions, и прописать в нем несколько удобных функций, которые будут возвращать ImageResult.

Примечание. Не забудьте импортировать пространство имен System.Runtime.CompilerServices.

VB .NET:
Public Module ControllerExtensions

  <Extension()> _
  Public Function Image(ByVal controller As System.Web.Mvc.Controller, ByVal imageStream As Stream, ByVal contentType As String) As ImageResult
    Return New ImageResult(imageStream, contentType)
  End Function

  <Extension()> _
  Public Function Image(ByVal controller As System.Web.Mvc.Controller, ByVal imageBytes() As Byte, ByVal contentType As String) As ImageResult
    Return New ImageResult(New MemoryStream(imageBytes), contentType)
  End Function

  <Extension()> _
  Public Function Image(ByVal controller As System.Web.Mvc.Controller, ByVal fileName As String, ByVal contentType As String) As ImageResult
    Return New ImageResult(System.IO.File.OpenRead("C:\kbyte.ru.gif"), contentType)
  End Function

End Module


C#:
public static class ControllerExtensions
{

  [Extension()]
  public static ImageResult Image(System.Web.Mvc.Controller controller, Stream imageStream, string contentType)
  {
    return new ImageResult(imageStream, contentType);
  }

  [Extension()]
  public static ImageResult Image(System.Web.Mvc.Controller controller, byte[] imageBytes, string contentType)
  {
    return new ImageResult(new MemoryStream(imageBytes), contentType);
  }

  [Extension()]
  public static ImageResult Image(System.Web.Mvc.Controller controller, string fileName, string contentType)
  {
    return new ImageResult(System.IO.File.OpenRead("C:\\kbyte.ru.gif"), contentType);
  }

}


Теперь в контроллерах можно использовать прописанные функции, например:

VB .NET:
Return Image(System.IO.File.OpenRead("C:\kbyte.ru.gif"), "image/gif")

C#:
return Image(System.IO.File.OpenRead("C:\\kbyte.ru.gif"), "image/gif");

Послесловие



Наследуясь от класса ActionResult можно вывести данные абсолютно любого формата. Какой функционал кода будет прописан в методе ExecuteResult, зависит полностью от вашей фантазии. Что касается изображений, если вы будете выводить изображения из файлов, я бы рекомендовал предварительно считывать их (делать копию, например, в массив байт), чтобы случайно не заблокировать файлы, если в дальнейшем потребуется производить над ними (над файлами) какие-либо манипуляции (перемещение, удаление).


Немиро Алексей,
29.06.2009
Tags:
Hubs:
Total votes 17: ↑12 and ↓5+7
Comments4

Articles