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.