asp.net-mvc - ASP.NET MVC控制器可以返回一个图像吗?





image controller (14)


我看到两个选项:

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

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

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

我希望通过一个控制器路由这个逻辑,每当需要如下的URL时:

www.mywebsite.com/resource/image/topbanner

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

我已经看到了这个例子,你必须创建一个视图 - 我不想使用视图。 我只想用Controller来完成这一切。

这可能吗?




看看ContentResult。 这会返回一个字符串,但可以用来制作自己的BinaryResult类。




为什么不简单并使用波浪号~操作符?

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



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

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




您可以直接写入响应,但不能测试。 最好返回一个延迟执行的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的发布版本,我做了以下工作:

[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倍(其他控制器avg 340ms,非控制器65ms)。




解决方案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 {[email protected]})}

要在任何HTML中使用此操作结果呈现的图像,请使用

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



稍作解释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;
    }






您可以使用HttpContext.Response并直接将内容写入它(WriteFile()可能适用于您),然后从Action而不是ActionResult返回ContentResult。

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




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

            }



您可以创建自己的扩展并按照这种方式进行。

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



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

希望有所帮助!




eu-ge-ne的修改实现。 这个基于应用程序中定义的路线返回一个生成的链接。 欧格尼的例子总是返回一个固定的答案。 下面的方法将允许您测试正确的操作/控制器和路由信息是否正在传递到UrlHelper中 - 如果您正在测试对UrlHelper的调用,这正是您想要的。

var context = new Mock<HttpContextBase>();
var request = new Mock<HttpRequestBase>();
var response = new Mock<HttpResponseBase>();
var session = new Mock<HttpSessionStateBase>();
var server = new Mock<HttpServerUtilityBase>();

context.Setup(ctx => ctx.Request).Returns(request.Object);
context.Setup(ctx => ctx.Response).Returns(response.Object);
context.Setup(ctx => ctx.Session).Returns(session.Object);
context.Setup(ctx => ctx.Server).Returns(server.Object);

request.SetupGet(x => x.ApplicationPath).Returns("/");
request.SetupGet(x => x.Url).Returns(new Uri("http://localhost/a", UriKind.Absolute));
request.SetupGet(x => x.ServerVariables).Returns(new NameValueCollection());

response.Setup(x => x.ApplyAppPathModifier(It.IsAny<string>())).Returns<string>(x => x);

context.SetupGet(x => x.Request).Returns(request.Object);
context.SetupGet(x => x.Response).Returns(response.Object);

var routes = new RouteCollection();
MvcApplication.RegisterRoutes(routes);
var helper = new UrlHelper(new RequestContext(context.Object, new RouteData()), routes);




asp.net asp.net-mvc image controller