Can an ASP.NET MVC controller return an Image?
我可以创建一个只返回图像资产的控制器吗?
每当请求如下URL时,我希望通过控制器路由此逻辑:
1 | www.mywebsite.com/resource/image/topbanner |
控制器将查找
我看到过这样的例子,你必须创建一个视图-我不想使用一个视图。我只想用控制器来完成这一切。
这有可能吗?
使用基本控制器文件方法。
1 2 3 4 5 6 | 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"); } |
值得注意的是,这似乎相当有效。我做了一个测试,通过控制器(
- MVC:每张照片7.6毫秒
- 直接:每张照片6.7毫秒
注意:这是请求的平均时间。平均值是通过在本地计算机上发出数千个请求来计算的,因此总数不应包括网络延迟或带宽问题。
使用MVC的发布版本,我要做的是:
1 2 3 4 5 6 7 | [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)。
稍微解释一下迪兰的反应:
三个类实现fileresult类:
1 2 3 4 | System.Web.Mvc.FileResult System.Web.Mvc.FileContentResult System.Web.Mvc.FilePathResult System.Web.Mvc.FileStreamResult |
它们都是不言自明的:
- 对于文件存在于磁盘上的文件路径下载,使用
FilePathResult –这是最简单的方法,避免了必须使用流。 - 对于byte[]数组(类似于response.binarywrite),使用
FileContentResult 。 - 对于要下载文件的byte[]数组(内容处置:附件),使用
FileStreamResult ,方法与下面的方法类似,但使用MemoryStream 和GetBuffer() 。 - 对于
Streams 使用FileStreamResult 。它被称为filestreamresult,但它需要一个Stream ,所以我想它可以与一个MemoryStream 一起工作。
下面是使用内容处置技术(未测试)的示例:
1 2 3 4 5 6 7 8 9 10 | [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; } |
如果您想在返回图像之前对其进行修改,这可能会有所帮助:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | 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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | 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")%> |
为什么不简单地使用tilde
1 2 3 | public FileResult TopBanner() { return File("~/Content/images/topbanner.png","image/png"); } |
您可以直接写入响应,但之后它就不可测试了。最好返回延迟执行的actionResult。以下是我的可恢复流结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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); } } |
更新:有比我原来的答案更好的选择。这在MVC之外工作得很好,但是最好坚持使用内置的返回图像内容的方法。查看投票结果。
你当然可以。尝试以下步骤:
下面是一些示例代码:
1 2 3 4 | string pathToFile = @"C:\Documents and Settings\some_path.jpg"; byte[] imageData = File.ReadAllBytes(pathToFile); Response.ContentType ="image/jpg"; Response.BinaryWrite(imageData); |
希望有帮助!
这对我很有用。因为我在SQL Server数据库中存储图像。
1 2 3 4 5 6 7 8 9 10 | [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; } |
在上面的代码片段中,
1 2 3 4 5 | public class FileImage { public string Uuid { get; set; } public byte[] Data { get; set; } public string ContentType { get; set; } } |
解决方案1:从图像URL在视图中呈现图像
您可以创建自己的扩展方法:
1 2 3 4 5 6 | public static MvcHtmlString Image(this HtmlHelper helper,string imageUrl) { string tag ="<img src='{0}'/>"; tag = string.Format(tag,imageUrl); return MvcHtmlString.Create(tag); } |
然后像这样使用:
1 | @Html.Image(@Model.ImagePath); |
解决方案2:从数据库渲染图像
创建一个控制器方法,返回如下图像数据
1 2 3 4 5 6 7 8 9 10 | 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); } } |
在以下视图中使用它:
1 | @ { Html.RenderAction("View","Image",new {[email protected]})} |
若要在任何HTML中使用从此操作结果呈现的图像,请使用
1 | <img src="http://something.com/image/view?id={imageid}> |
您可以使用文件返回文件,如视图、内容等
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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(" Patient Clinical Document Not Uploaded"); } } |
看看contentresult。这将返回一个字符串,但可以用于生成自己的类似于binaryResult的类。
1 2 3 4 | if (!System.IO.File.Exists(filePath)) return SomeHelper.EmptyImageResult(); // preventing JSON GET/POST exception else return new FilePathResult(filePath, contentType); |
如果文件存储在本地驱动器上,这是最简单的方法。如果文件是
您可以使用httpContext.response并直接将内容写入其中(writeFile()可能对您有效),然后从操作返回contentResult而不是actionResult。
免责声明:我没有尝试过,它是基于查看可用的API。-)
我看到两种选择:
1)实现自己的iviewEngine,并在所需的"image"方法中将所用控制器的viewEngine属性设置为imageviewEngine。
2)使用视图-)。只需更改内容类型等。