我可以创建一个仅返回图像资产的控制器吗?
每当请求以下URL时,我都希望通过控制器路由此逻辑:
www.mywebsite.com/resource/image/topbanner
控制器将查找topbanner.png
并将该图像直接发送回客户端。
我看过这样的示例,其中您必须创建一个View-我不想使用View。我想只用Controller来完成所有操作。
这可能吗?
ApiController class
我可以创建一个仅返回图像资产的控制器吗?
每当请求以下URL时,我都希望通过控制器路由此逻辑:
www.mywebsite.com/resource/image/topbanner
控制器将查找topbanner.png
并将该图像直接发送回客户端。
我看过这样的示例,其中您必须创建一个View-我不想使用View。我想只用Controller来完成所有操作。
这可能吗?
ApiController class
Answers:
使用基本控制器的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
)请求图像,结果是:
注意:这是请求的平均时间。平均值是通过在本地计算机上发出数千个请求来计算的,因此总数不应包含网络延迟或带宽问题。
App_Data
其中包含应由应用程序的某些用户而非其他用户签名的图像。使用控制器动作为它们提供服务可以限制访问。
/../../../danger/someFileTheyTHoughtWasInaccessible
使用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倍(其他控制器平均340毫秒,非控制器65毫秒)。
Path.Combine
代替concat来获得更安全,更易读的代码。
为了稍微解释Dyland的反应:
三个类实现FileResult类:
System.Web.Mvc.FileResult
System.Web.Mvc.FileContentResult
System.Web.Mvc.FilePathResult
System.Web.Mvc.FileStreamResult
它们都是很自我解释的:
FilePathResult
-这是最简单的方法,避免了您必须使用Streams。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;
}
如果您想在返回之前修改图像,这可能会有所帮助:
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<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")%>
您可以直接写入响应,但无法测试。最好返回延迟执行的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);
}
}
更新:有比我原始答案更好的选择。这在MVC之外效果很好,但是最好坚持使用内置的返回图像内容的方法。查看投票结果。
当然可以。尝试以下步骤:
这是一些示例代码:
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 = "<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}>
这对我有用。由于我将图像存储在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; }
}
您可以使用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>"); }
}
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建议使用FileContentResult
或FileStreamResult
。
我看到两个选择:
1)实现自己的IViewEngine,并以所需的“图像”方法将要使用的Controller的ViewEngine属性设置为ImageViewEngine。
2)使用视图:-)。只需更改内容类型等。
下面的代码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");
}
我也遇到类似的要求,
因此,在我的情况下,我向控制器发出了带有图像文件夹路径的请求,该路径随后返回一个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>";
然后在Controller中,通过图像路径生成图像并将其返回给调用者
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();
}
读取图像,将其转换为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;