ASP.NET MVC 컨트롤러가 이미지를 반환 할 수 있습니까?


455

단순히 이미지 자산을 반환하는 컨트롤러를 만들 수 있습니까?

다음과 같은 URL이 요청 될 때마다 컨트롤러를 통해이 논리를 라우팅하고 싶습니다.

www.mywebsite.com/resource/image/topbanner

컨트롤러는 topbanner.png해당 이미지를 찾아 클라이언트로 직접 보냅니다.

View를 작성 해야하는 곳의 예를 보았습니다 .View를 사용하고 싶지 않습니다. 컨트롤러만으로 모든 것을하고 싶습니다.

이것이 가능한가?


1
나는 /programming/155906/creating-a-private-photo-gallery-using-aspnet-mvc 에서 비슷한 질문 을하고이를 수행하기위한 훌륭한 가이드를 찾았습니다. 이 가이드에 따라 ImageResult 클래스를 만들었습니다. https://blog.maartenballiauw.be/post/2008/05/13/aspnet-mvc-custom-actionresult.html
Vyrotek

2
이미지를 수정 하려면 최상의 성능을 위해 ImageResizing.Net HttpModule사용하십시오 . 그렇지 않으면 FilePathResult가 오버 헤드의 몇 퍼센트 만 추가합니다. URL 재 작성은 약간 줄어 듭니다.
Lilith River

1
MVC 대신 WebApi Controller를 사용하지 않는 이유는 무엇입니까? ApiController class
A-Sharabiani 2016 년

답변:


534

기본 컨트롤러 파일 방법을 사용하십시오.

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 밀리 초

참고 : 이것은 평균 요청 시간입니다. 평균은 로컬 컴퓨터에서 수천 건의 요청을 수행하여 계산되었으므로 총계에는 네트워크 대기 시간이나 대역폭 문제가 포함되지 않아야합니다.


10
지금이 질문에 나오는 사람들에게는 이것이 나에게 가장 적합한 솔루션이었습니다.
Clarence Klopfstein 2009

177
이것은 안전한 코드가 아닙니다. 사용자가 이와 같이 파일 이름 (경로)을 전달하게하면 서버의 어느 곳에서나 파일에 액세스 할 수 있습니다. 사람들에게 그대로 사용하지 말라고 경고 할 수 있습니다.
Ian Mercer

7
파일이 필요할 때 즉석에서 구성하고 파일이 만들어지면 캐싱하지 않는 한 (그것이 우리가하는 것입니다).
Brian

15
@ mare- 당신은 제한된 위치에서 파일을 제공하는 경우에도 이것을 할 수 App_Data있습니다. 컨트롤러 작업을 사용하여 액세스를 제한 할 수 있습니다.
Russ Cam

8
다른 언급 한 바와 같이, 사용자가 신중하게 구성된 POST 또는 쿼리 문자열을 사용하여 디렉토리를 탐색 할 수있게하는 실제 프로덕션 코드를 보았으므로 경로 작성에주의하십시오. /../../../danger/someFileTheyTHoughtWasInaccessible
AaronLS

128

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 밀리 초였습니다 (컨트롤러 평균은 68ms, 비 컨트롤러는 65ms).

나는 대답에 언급 된 다른 방법 중 일부를 시도했지만 성능 적중은 훨씬 더 극적이었습니다 ... 솔루션 중 일부는 컨트롤러가 아닌 6 배 (다른 컨트롤러는 평균 340ms, 컨트롤러가 아닌 65ms)였습니다.


12
이미지가 수정되지 않은 것은 어떻습니까? 마지막 요청 이후 이미지가 수정되지 않으면 FileStreamResult가 304를 보내야합니다.
dariol

Path.Combine더 안전하고 읽기 쉬운 코드를 위해 concat 대신 사용할 수 있습니다 .
Marcell Toth

101

Dyland의 응답을 약간 넓히려면 :

세 가지 클래스가 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아래와 비슷한 방식으로 a MemoryStream및 using을 사용하십시오 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;
    }

2
이 게시물의 내용 처리 부분은 매우 유용했습니다.
Diego

VS는 FileStream ()의 과부하가 더 이상 사용되지 않는다고 알려줍니다.
MrBoJangles

1
참고 사항 : 파일 이름에 쉼표가 있으면 Chrome에서 '너무 많은 헤더를 수신했습니다'라는 오류와 함께 쉼표를 거부합니다. 따라서 모든 쉼표를 "-"또는 ""로 바꾸십시오.
Chris S

웹 API 컨트롤러 만 사용하여이를 수행하는 방법은 무엇입니까?
Zapnologica

74

이미지를 반환하기 전에 수정하려는 경우에 도움이 될 수 있습니다.

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

1
감사합니다. 이는 클라이언트 측에서 수행 할 수없는 인증이 필요한 이미지를 다운로드하기 위해 프록시가 필요한 시나리오에 적합합니다.
Hong

1
Font, SolidBrush 및 Image라는 3 가지 기본 개체를 폐기하는 것을 잊고 있습니다.
Wout

3
제안 된 개선 사항 : 메모리 스트림을 만들고 데이터를 작성한 다음 .ToArray ()를 사용하여 데이터로 파일 결과를 만듭니다. ms.Seek (0, SeekOrigin.Begin)을 호출 한 다음 File (ms, " image / png ") // 스트림 자체를 반환
Quango

12

당신은 당신의 자신의 확장을 생성 하고이 방법을 수행 할 수 있습니다.

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

10

응답에 직접 쓸 수는 있지만 테스트 할 수는 없습니다. 실행이 지연된 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);
    }
}

9

왜 물결표 ~연산자를 사용하지 않습니까?

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

6

업데이트 : 원래 답변보다 더 나은 옵션이 있습니다. 이것은 MVC 외부에서 잘 작동하지만 이미지 내용을 반환하는 기본 제공 방법을 사용하는 것이 좋습니다. 투표율이 높은 답변을 참조하십시오.

당신은 확실히 할 수 있습니다. 다음 단계를 시도하십시오.

  1. 디스크에서 이미지를 바이트 배열로로드
  2. 이미지에 대한 더 많은 요청을 예상하고 디스크 I / O를 원하지 않는 경우 이미지를 캐시하십시오 (샘플은 아래에서 캐시하지 않습니다)
  3. Response.ContentType을 통해 MIME 유형 변경
  4. Response.Binary 이미지 바이트 배열을 작성합니다

샘플 코드는 다음과 같습니다.

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

희망이 도움이됩니다!


4
그리고 이것이 컨트롤러의 행동에서 어떻게 보일까요?
CRice

5

해결 방법 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 {id=@Model.ImageId})}

이 작업 결과에서 렌더링 된 이미지를 HTML로 사용하려면

<img src="http://something.com/image/view?id={imageid}>

5

이것은 나를 위해 일했습니다. 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; }
}

4

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

            }

3

ContentResult를보십시오. 문자열을 반환하지만 고유 한 BinaryResult와 같은 클래스를 만드는 데 사용할 수 있습니다.


2
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또는 FileStreamResultDylan이 제안한대로 또는를 사용하십시오 .


1

두 가지 옵션이 있습니다.

1) 자신의 IViewEngine을 구현하고 사용중인 컨트롤러의 ViewEngine 속성을 원하는 "image"방법으로 ImageViewEngine에 설정하십시오.

2)보기를 사용하십시오 :-). 콘텐츠 유형 등을 변경하십시오.


1
추가 공간이나보기의 CRLF로 인해 문제가 발생할 수 있습니다.
엘란 하손

2
내 마지막 게시물에서 잘못되었습니다 ... msdn.microsoft.com/en-us/library/… WebImage 클래스와 WebImage.Write를 볼 수 있습니다 :)
Elan Hasson September

1

HttpContext.Response를 사용하여 직접 컨텐츠를 작성하고 (WriteFile ()이 효과가있을 수 있음) ActionResult 대신 조치에서 ContentResult를 리턴 할 수 있습니다.

면책 조항 : 나는 이것을 시도하지 않았으며 사용 가능한 API를 살펴 보았습니다. :-)


1
네, 방금 ContentResult가 문자열 만 지원한다는 것을 알았습니다. 그러나 자신 만의 ActionResult 기반 클래스를 만드는 것은 쉽습니다.
leppie

1

아래 코드는 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");
}

0

또한 비슷한 요구 사항이 발생했습니다.

따라서 필자의 경우 이미지 폴더 경로를 사용하여 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();
}

0

이미지를 읽고로 변환 byte[]한 다음 File()컨텐츠 유형으로 a 를 리턴하십시오 .

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;
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.