提问



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


我想通过控制器路由此逻辑,只要请求以下URL:


www.mywebsite.com/resource/image/topbanner


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


我已经看过这样的例子,你必须创建一个视图 - 我不想使用视图。我想用Controller完成所有操作。


这可能吗?

最佳参考


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



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

其它参考1


使用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毫秒)。

其它参考2


简要介绍Dyland的回应:


三个类实现了FileResult类:[40]


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


他们完全自我解释:



  • 对于文件存在于磁盘上的文件路径下载,请使用FilePathResult - 这是最简单的方法,避免使用Streams。

  • 对于byte [[]]数组(类似于Response.BinaryWrite),请使用FileContentResult

  • 对于您希望文件下载的byte [[]]数组(内容处理:附件),使用FileStreamResult以类似于下面的方式,但使用MemoryStream并使用GetBuffer()]]

  • Streams使用FileStreamResult。它被称为FileStreamResult但它需要Stream所以我d 猜测它与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;
    }

其它参考3


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


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

其它参考4


您可以创建自己的扩展并以此方式执行。


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

其它参考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


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


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

其它参考7


更新:有比我原来的答案更好的选择。这在MVC之外工作得很好,但最好坚持使用内置的返回图像内容的方法。查看最多投票的答案。


你当然可以。尝试以下步骤:



  1. 将映像从磁盘加载到字节数组

  2. 如果您希望对图像有更多请求并且不想要磁盘I/O(我的示例不会将其缓存在下面),则缓存图像。

  3. 通过Response.ContentType
  4. 更改mime类型
  5. Response.BinaryWrite out image byte array



这是一些示例代码:


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


希望有所帮助!

其它参考8


看看ContentResult。这将返回一个字符串,但可用于创建自己的BinaryResult类。

其它参考9


解决方案1:在图片网址的视图中呈现图片


您可以创建自己的扩展方法:


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中使用从此actionresult渲染的图像,请使用


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

其它参考10


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 - 则使用FileContentResultFileStreamResult作为Dylan建议。

其它参考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


我看到两个选择:


1)实现您自己的IViewEngine,并在您想要的图像方法中将您正在使用的Controller的ViewEngine属性设置为ImageViewEngine。


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

其它参考13


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


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

其它参考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>"); }

            }