我可以创建一个只返回图像资产的控制器吗?
我想通过控制器路由此逻辑,只要请求以下URL:
www.mywebsite.com/resource/image/topbanner
控制器将查找topbanner.png
并将该图像直接发送回客户端.
我已经看到了这个例子,你必须创建一个View - 我不想使用View.我想用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"); }
需要注意的是,这似乎相当有效.我做了一个测试,我通过controller(http://localhost/MyController/Image/MyImage
)和直接URL(http://localhost/Images/MyImage.jpg
)请求图像,结果是:
MVC:每张照片7.6毫秒
直接:每张照片6.7毫秒
注意:这是请求的平均时间.通过在本地计算机上发出数千个请求来计算平均值,因此总计不应包括网络延迟或带宽问题.
使用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倍(其他控制器平均340ms,非控制器65ms).
稍微解释一下Dyland的反应:
三个类实现了FileResult类:
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
所以我猜它适用于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; }
如果您想在返回之前修改图像,这可能会有所帮助:
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"); }
您可以创建自己的扩展并以此方式执行.
public static class ImageResultHelper { public static string Image(this HtmlHelper helper, Expression > action, int width, int height) where T : Controller { return ImageResultHelper.Image (helper, action, width, height, ""); } public static string Image (this HtmlHelper helper, Expression > 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 (helper.ViewContext.RequestContext, helper.RouteCollection, action); return string.Format("", 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 (c => c.Index(), 120, 30, "Current time")%>
您可以直接写入响应,但它不可测试.最好返回已延迟执行的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); } }
为什么不简单并使用波浪号~
运算符?
public FileResult TopBanner() { return File("~/Content/images/topbanner.png", "image/png"); }
更新:有比我原始答案更好的选择。这在MVC之外效果很好,但是最好坚持使用返回图像内容的内置方法。查看投票结果。
当然可以。尝试以下步骤:
将磁盘中的图像加载到字节数组中
如果您希望对图像有更多请求并且不希望磁盘I / O(我的示例不在下面对其进行缓存),则可以缓存该图像
通过Response.ContentType更改MIME类型
Response.Binary写出图像字节数组
这是一些示例代码:
string pathToFile = @"C:\Documents and Settings\some_path.jpg"; byte[] imageData = File.ReadAllBytes(pathToFile); Response.ContentType = "image/jpg"; Response.BinaryWrite(imageData);
希望有帮助!
解决方案1:从图像URL在视图中呈现图像
您可以创建自己的扩展方法:
public static MvcHtmlString Image(this HtmlHelper helper,string imageUrl) { string tag = ""; 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格式使用通过此操作呈现的图像,请使用
[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; } }