7

我正在使用 ASP.NET MVC4 构建一个网站来上传/显示图像,并找到了几种显示图像的方法,主要是以下 -

1)将图片作为文件存储在服务器本地,并使用相对路径在页面上显示。

 //ImageFile is a property which holds a path to the image in the model
 <img src="@item.ImageFile" />

 When the page is rendered, this becomes a path like - <img src="/Content/Images/Jellyfish.jpg" />      

2)将图像存储为字节数组并使用控制器操作检索它 -

//Use a controller action (GetImg) to get the URL of the image
<img src="@Url.Action("GetImg", "ViewPhotos", new { id = item.ID })" alt="Image" />  

When the page is rendered, this becomes a path like - <img src="/ViewPhotos/GetImg/4" alt="Image" />    

3)将图像存储为字节数组并直接在视图中检索它 -

//Directly re-construct the image in the view from the byte array (Image property of the model)
<img src="@String.Format("data:image/jpg;base64,{0}", Convert.ToBase64String(item.Image))" />  

When the page is rendered, this becomes a path like - <img src="data:image/jpg;base64,/9j/4AAQSkZJRgABAgEA....<long string> .../>

我的问题是——

1) 1 和 2 有什么区别?

1 直接提供文件的路径,2 提供文件的 URL。它们在背景中是相同的还是一种方法比另一种更好?

我在这里检查过,它说 - Url.Action 将构造动作的路径,返回一个 url,而不是执行动作的结果。那么什么时候检索结果呢?

2) 当 3 用于 1 或 2 时,它会对性能产生影响吗?

3) 当图像尺寸较小(小于 1MB)或较大时,应该使用哪种方法?

如果您可以向我指出任何可以提供帮助的链接,我会很高兴。谢谢。

代码 -

//模型

public class Photo
{
  public int ID { get; set; }
  public string ImageFile { get; set; }
  public byte[] Image { get; set; }
  public string Caption { get; set; }
}

//控制器

public FileContentResult GetImg(int id)
{
    byte[] byteArray = db.Photos.Find(id).Image;
    if (byteArray != null)
    {
        return new FileContentResult(byteArray, "image/jpeg");
    }
    else
    {
         return null;
    }
}

//查看(方法二)

@model IEnumerable<MyPhotoLibrary.Models.Photo>
@foreach (var item in Model) {
<tr>
  <td>
    <img src="@Url.Action("GetImg", "ViewPhotos", new { id = item.ID })" alt="Image" />
  </td>
</tr>
}

//查看(这是方法3)

@model IEnumerable<MyPhotoLibrary.Models.Photo>
@foreach (var item in Model) {
<tr>
  <td>
    <img src="@String.Format("data:image/jpg;base64,{0}", Convert.ToBase64String(item.Image))" />
  </td>
</tr>
}
4

3 回答 3

14

我最近自己查了一下,发现了 Microsoft 研究论文To BLOB Or Not To BLOB,它比较了将文件(例如图像)作为二进制数据存储在数据库中的性能与传统文件系统的性能。

调查结果大致概括如下:

  • 小文件(大小 > 256KB)最好以字节数组的形式存储在数据库中(您的第二个和第三个选项),从中可以快速检索它们并提供给用户。
  • 大文件(大小 > 1MB 左右)最好存储在文件系统中(您的第一个选择),因为大型数据库 blob 的吞吐量和碎片随着文件的增大而急剧下降
  • 介于 256KB 和 1MB 之间(大致;范围很模糊,取决于您的确切设置)性能取决于文件可能被编辑或覆盖的次数;一般来说,数据库在处理静态文件时表现得更好,而文件系统在保持高吞吐量和低碎片化频繁更改的文件方面要好得多。

选项之间的其他区别:

选项 1要求您的应用程序对图像所在的任何文件夹都具有读取权限,如果图像正在由用户上传或编辑,则应用程序也需要对该文件夹具有写入权限。如果您使用共享主机(尤其是廉价或免费主机计划)或者您的系统管理员特别偏执,这可能会很棘手。

选项 2需要从客户端到服务器的更多调用以获取所有数据;这可能看起来像一件坏事,但它确实将页面加载分解成可能实际上有帮助的块 - 页面(没有图像)将在第一次调用时加载,然后将单独请求图像,如果其中一个需要一段时间,或者有某种错误,那么页面的其余部分应该不受影响,这很好。正如jgauffin 在评论中指出的那样,此选项允许您为图像设置缓存,因此浏览器可以通过仅在更改时重新下载它们来节省带宽。

选项 3要求客户端一次性加载整个批次,这可能需要一段时间。它还将字节数组的处理转移到客户端,这可能是智能手机或低端上网本等低功耗客户端的问题。如果您的图像很大,最好使用服务器的 CPU 能力来实际处理从字节数组到图像文件的转换。正如jgauffin在评论中指出的那样,这也不能很好地与缓存一起使用 - 如果视图的 HTML 的任何部分发生更改,浏览器将不得不再次下载图像

此外,这可能与您的具体情况无关,但通过单独调用加载图像可以让您在这些操作中执行其他操作,例如写入调试日志或在幕后进行其他一些内务处理。

于 2013-05-31T11:21:46.423 回答
2

1) 在服务器端的处理会更少,因为函数调用会更少。如果你想渲染动作的输出 - 你需要调用 HTML 帮助器方法 Html.RenderAction 或 Html.Action 更多关于这里的差异

2) 2 和 3 之间的区别在于,当您直接输入字节数组时,您到服务器的往返次数更少(DNS 查找等),但在单个请求中下载的数据更多。某些浏览器可能会并行下载内容(每个域 5-8 次并行下载),因此如果您使用 3,则会在页面加载速度方面松动。更多关于并行下载herehere

这导致我们3)如果你有小图像,3是要走的路,因为客户端会请求更少的资源,页面加载速度会更快,但如果你有大图像,你应该使用1。

于 2013-05-31T11:02:43.767 回答
1

我没有时间测试性能,但我会告诉你几点。

在方法 1 中,您确实仅依靠 HDD 存储和性能来保存文件以及您的 Web 服务器提供静态文件的能力。

在方法 2 中,每次有请求时都会生成字节流……存储和检索是 DB 驱动的(当然是 HDD 起作用),如果不添加一些相关代码,就无法利用 Web 服务器的本机缓存功能(这可能只是一个属性OutputCache,但它可能更复杂)。

在方法 3 中,客户端只是在渲染,而您正在将图像与网页一起发送。这里的性能实际上取决于浏览器,但请记住,如果图像非常大,接收整个页面需要一些时间。

所以我通常更喜欢方法 1,我只需要在数据库中存储一个图像路径,然后我可以从磁盘获取图像并让 IIS 进行缓存、传递、优化等。

选择方法 2 有很多正当理由,主要是如果您有一个分布式 Web 服务器架构,其中方法 1 意味着每个服务器必须在 HDD 上保存图像的副本,同时将所有内容放在一个地方在数据库中意味着您可以随时检索图像...也许实施一些缓存以避免在每个请求中命中数据库。

方法 3 实际上仅适用于一次性使用的小图像。我说一次性使用是因为你不想将它用于周围使用的图标和图像,它们应该保持静态,以便客户端可以缓存它们。

希望这有助于做出更好的决定。

在类似于 2 的场景中,具有多个 Web 服务器架构,在过去的工作中,我们采用了混合解决方案:图像在 DB 上,但每个单个 Web 服务器,在图像达到 404 后的第一个请求时,将从数据库下载图像并指示客户端重试(在某种程度上,这是一种非常原始的缓存实现)。

于 2013-05-31T11:25:21.640 回答