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

我想通过控制器路由这个逻辑,每当请求如下URL时:

www.mywebsite.com/resource/image/topbanner

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

我见过这样的例子,你必须创建一个视图-我不想使用视图。我想只用控制器来做。

这可能吗?


当前回答

您可以直接写入响应,但这样它就不可测试了。最好返回一个延迟执行的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);
    }
}

其他回答

解决方案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中使用此actionresult渲染的图像,请使用

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

使用基本控制器文件方法。

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毫秒

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

使用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倍(其他控制器平均340ms,非控制器65ms)。

从Core 3.2下的字节[],你可以使用:

public ActionResult Img(int? id) {
    MemoryStream ms = new MemoryStream(GetBytes(id));
    return new FileStreamResult(ms, "image/png");
}

这对我很管用。 因为我将图像存储在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; }
}