当前位置:  开发笔记 > 后端 > 正文

ASP.NET MVC控制器可以返回一个Image吗?

如何解决《ASP.NETMVC控制器可以返回一个Image吗?》经验,为你挑选了10个好方法。

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

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

www.mywebsite.com/resource/image/topbanner

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

我已经看到了这个例子,你必须创建一个View - 我不想使用View.我想用Controller完成所有操作.

这可能吗?



1> Brian..:

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

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


这不是安全的代码.让用户传递这样的文件名(路径)意味着他们可以从服务器上的任何位置访问文件.可能要警告人们不要按原样使用它.
@ mare-如果您从受限制的位置提供文件,您也可以这样做,例如,您可能在"App_Data"中有图像应该由您的应用程序的某些用户签名而不是其他用户.使用控制器操作为其提供服务允许您限制访问.
对于那些现在提出这个问题的人来说,这是最适合我的解决方案.
除非您在需要时动态构建文件,并在创建文件后对其进行缓存(这就是我们的工作).
正如其他人所提到的那样,在你的路径构建中要谨慎,因为我已经看到了允许用户使用精心构造的POST或查询字符串导航目录的实际生产代码:`/../../../anger/someFileTheyTHoughtWasInaccessible `
我想如果你想把你的问题选为"答案",你必须在同一年回答它:).
我不是肯定的,但对此解决方案的快速测试表明它忽略了"If-Modified-Since"标题并将始终返回文件.在某些情况下这可能是可取的,但它会绕过客户端缓存并损害性能.
很棒的答案Brian.有一件事,File()方法调用中的contentType参数应该是示例中的"image/jpeg".IE8会弹出一个下载对话框.还应该选择这个答案.

2> Sailing Judo..:

使用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).


那么图像没有被修改?自上次请求后未修改图像时,FileStreamResult应发送304.

3> Chris S..:

稍微解释一下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;
    }


这篇文章的内容处理部分非常有用

4> staromeste..:

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

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


这里建议的改进:你创建一个内存流,写入数据然后使用.ToArray()创建一个带有数据的文件结果你也可以只调用ms.Seek(0,SeekOrigin.Begin)然后返回File(ms," image/png")//返回流本身

5> Oleksandr Fe..:

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

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("\"{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(c => c.Index(), 120, 30, "Current time")%>



6> JarrettV..:

您可以直接写入响应,但它不可测试.最好返回已延迟执行的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);
    }
}



7> JustinStolle..:

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

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



8> Ian Suttle..:

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

希望有帮助!


在控制器的动作中会如何看待?

9> Ajay Kelkar..:

解决方案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; }
}

推荐阅读
小色米虫_524
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有