【问题标题】:Can an ASP.NET MVC controller return an Image?ASP.NET MVC 控制器可以返回图像吗?
【发布时间】:2010-09-16 05:02:04
【问题描述】:

我可以创建一个只返回图像资源的控制器吗?

每当请求如下 URL 时,我想通过控制器路由此逻辑:

www.mywebsite.com/resource/image/topbanner

控制器将查找topbanner.png 并将该图像直接发送回客户端。

我已经看到了必须创建视图的示例 - 我不想使用视图。我想只用控制器来完成这一切。

这可能吗?

【问题讨论】:

标签: asp.net asp.net-mvc image controller


【解决方案1】:

我看到两个选项:

1) 实现您自己的 IViewEngine 并将您正在使用的控制器的 ViewEngine 属性设置为您所需的“图像”方法中的 ImageViewEngine。

2) 使用视图:-)。只需更改内容类型等。

【讨论】:

  • 由于视图中有多余的空格或 CRLF,这可能会导致问题。
  • 我上一篇文章错了...msdn.microsoft.com/en-us/library/…您可以在视图中使用 WebImage 类和 WebImage.Write :)
【解决方案2】:

查看内容结果。这将返回一个字符串,但可用于创建您自己的类 BinaryResult 类。

【讨论】:

    【解决方案3】:

    您可以使用 HttpContext.Response 并直接将内容写入其中(WriteFile() 可能对您有用),然后从您的操作而不是 ActionResult 返回 ContentResult。

    免责声明:我没有尝试过,它是基于查看可用的 API。 :-)

    【讨论】:

    • 是的,我刚刚注意到 ContentResult 只支持字符串,但创建自己的基于 ActionResult 的类很容易。
    【解决方案4】:

    更新:有比我原来的答案更好的选择。这在 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);
    

    希望有帮助!

    【讨论】:

    • 这在控制器的动作中看起来如何?
    【解决方案5】:

    您可以直接写入响应,但它是不可测试的。最好返回一个延迟执行的 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);
        }
    }
    

    【讨论】:

      【解决方案6】:

      使用 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。
      • 您可以使用Path.Combine 代替 concat 以获得更安全、更易读的代码。
      【解决方案7】:

      使用基本控制器的 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 毫秒

      注意:这是请求的平均时间。平均值是通过在本地计算机上发出数千个请求计算得出的,因此总数不应包括网络延迟或带宽问题。

      【讨论】:

      • 对于那些现在提出这个问题的人来说,这是最适合我的解决方案。
      • 这不是安全代码。让用户像这样传递文件名(路径)意味着他们可能会从服务器上的任何位置访问文件。可能想警告人们不要按原样使用它。
      • 除非您根据需要即时构建文件并在创建后缓存它们(这就是我们所做的)。
      • @mare- 如果您从受限位置提供文件,您也可以这样做,例如您可能在App_Data 中有图像,应该由您的应用程序的某些用户而不是其他用户签名。使用控制器操作为它们提供服务允许您限制访问。
      • 正如其他人所提到的,在构建路径时要小心,因为我已经看到实际的生产代码允许用户使用精心构建的 POST 或查询字符串向上导航目录:/../../../danger/someFileTheyTHoughtWasInaccessible
      【解决方案8】:

      稍微解释一下Dyland的回应:

      三个类实现FileResult类:

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

      它们都是不言自明的:

      • 对于文件存在于磁盘上的文件路径下载,请使用 FilePathResult - 这是最简单的方法,您不必使用 Streams。
      • 对于 byte[] 数组(类似于 Response.BinaryWrite),请使用 FileContentResult
      • 对于您要下载文件的字节[] 数组(内容处置:附件),使用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;
          }
      

      【讨论】:

      • 这篇文章的内容配置部分非常有帮助
      • VS 告诉我 FileStream() 的这种重载已经过时了。
      • 注意事项:如果您的文件名中有逗号,Chrome 会以“收到的标头太多”错误拒绝它。所以用“-”或“”替换所有逗号。
      【解决方案9】:
      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 - 然后按照 Dylan 的建议使用 FileContentResultFileStreamResult

      【讨论】:

        【解决方案10】:

        如果您想在返回图像之前对其进行修改,这可能会有所帮助:

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

        【讨论】:

        • 谢谢。这非常适合需要代理来下载无法在客户端完成的需要身份验证的图像的场景。
        • 您忘记处理多达 3 个原生对象:Font、SolidBrush 和 Image。
        • 这里建议的改进:您创建一个内存流,写入数据,然后使用 .ToArray() 使用数据创建一个文件结果您也可以只调用 ms.Seek(0, SeekOrigin.Begin)然后 return File(ms, "image/png") // 返回流本身
        【解决方案11】:

        您可以创建自己的扩展程序并这样做。

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

        【讨论】:

          【解决方案12】:

          为什么不简单地使用波浪号~ 运算符?

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

          【讨论】:

            【解决方案13】:

            解决方案 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}>
            

            【讨论】:

              【解决方案14】:

              您可以使用 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>"); }
              
                          }
              

              【讨论】:

                【解决方案15】:

                这对我有用。 因为我将图像存储在 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;
                    }
                

                在上面的sn-p中_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; }
                }
                

                【讨论】:

                  【解决方案16】:

                  我也遇到过类似的需求,

                  因此,在我的情况下,我使用图像文件夹路径向 Controller 发出请求,然后返回一个 ImageResult 对象。

                  以下代码 sn -p 说明工作:

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

                  【讨论】:

                    【解决方案17】:

                    下面的代码使用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");
                    }
                    

                    【讨论】:

                    • 已测试,但更正了此代码示例中的一个小错误。使用了编辑。“结果”在 Save 语句中应该是“位图”。非常有用的例子,谢谢! +1
                    【解决方案18】:

                    读取图像,将其转换为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;
                    

                    【讨论】:

                      【解决方案19】:

                      是的,您可以返回图片

                      public ActionResult GetImage(string imageFileName)
                      {
                          var path = Path.Combine(Server.MapPath("/Images"), imageFileName + ".jpg"); 
                          return base.File(path, "image/jpeg");
                      }
                      

                      (请不要忘记将此标记为答案)

                      【讨论】:

                        猜你喜欢
                        • 2012-02-28
                        • 1970-01-01
                        • 2013-11-10
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 2010-11-04
                        • 1970-01-01
                        • 2016-02-29
                        相关资源
                        最近更新 更多