跳转到内容

WebObjects/Web 应用程序/开发/数据库与文件系统

来自 Wikibooks,开放世界中的开放书籍

关于是否将媒体存储在数据库中,还是将媒体存储在文件系统中并在数据库中仅存储引用,存在着持续的争论,通常与媒体文件相关。

本文试图追踪一些关于这些争论的著名文章。

Joe Moreno

[编辑 | 编辑源代码]

请记住,数据库的目的是存储要搜索和检索的数据。
您实际向数据库发送包含图像 blob 的查询的情况很少见(例如,搜索与特定二进制数据匹配的图像)。更可能的是,您会根据图像的元数据(如日期、时间、图像名称或文件系统路径)执行搜索。一个好的解决方案是存储指向媒体的路径,然后只需构建客户端浏览器引用的引用 URL,或者让应用程序从文件系统中检索媒体并通过 WebObjects 适配器提供它。在前一种情况下,您可以将媒体保存在 Web 服务器下(例如图像缩略图),而在后一种情况下,您可以将全尺寸图像保存在服务器文件系统的任何其他位置,并根据用户的个人资料提供它们(例如,他们是否成功检出?等等)。

Michael Engelhart

[编辑 | 编辑源代码]

在我看来,将图像存储在数据库中通常是一个坏主意。从数据库中检索图像数据比让 Apache 提供图像的开销要大得多。Apache 针对此目的进行了高度优化。数据库通常没有。

我的建议是只在数据库中存储图像的 URL,并将该 URL 写入动态页面。或者,如果您知道路径始终保持不变,您也可以只存储文件名。

Robert Walker

[编辑 | 编辑源代码]

似乎不同的人对此主题有不同的看法。我已经关注了关于此主题的几个主题,但我仍然没有就最佳设计模式得出结论。

我不想将图像放在网络上的共享点上,并链接到图像目录。这将创建一个单点故障。如果您因任何原因丢失了与存储图像的盒子的连接,那么您将无法访问这些图像。如果将图像存储在数据库中,您可以使用数据库集群来提供一些冗余和故障转移支持。

无论哪种情况,您都将通过共享直接传输图像的二进制数据,或通过数据库连接传输。我个人采取简单的方法,将图像存储在数据库中。

示例:假设我有一个 Product 实体,并且想要上传和存储产品照片:我会创建两个实体 Product 和 ProductPhoto。然后,我会使用 toOne 或 toMany 关系将它们关联起来,具体取决于每个 Product 对象是否需要一个或多个 ProductPhoto 对象。

使用此设计模式,获取 Product 数据不会直接加载图像。相反,EOF 将创建代表图像的错误。只有在访问 ProductPhoto 错误对象时触发错误,才会获取图像数据。因此,如果您获取 500 个产品并将它们批处理到 WODisplayGroup 中的 10 个组,那么您的第一页将只获取前 10 个图像,而不是 500 个(并且只有在存在访问图像数据的 WOElement? 或方法的情况下)。

此模式还极大地简化了上传和存储图像,因为您可以将用于上传图像的 NSData 绑定到 ProductPhoto 的 imageData BLOB。

许多人可能不同意我的观点,但对于我的目的,此设计模式取得了很好的成功。

您可以在 /Developer/Examples/JavaWebObjects/Frameworks 中的 JavaRealEstate 框架示例中找到针对 toOne 和 toMany 照片的此设计模式的实现。

Michael Halliday

[编辑 | 编辑源代码]

将图像存储在我们的数据库 (OpenBase) 中,我没有任何问题。我们开发了许多基于“社区”的网站,其中包括照片集和在线约会服务,它们都使用罗伯特在他的消息中谈到的相同方法。

我必须说,我们没有遇到任何性能问题,对我来说,这是最优雅、最可扩展的解决方案,原因如下:

  • 您可以使用所有可用的数据库集群选项来实现数据冗余和数据备份。
  • 如果您的网站规模大幅增长,并且您需要多个 HTTP 服务器(以及多个应用程序服务器),如果您的图像由本地文件系统中的 Apache 提供,那么您将遇到必须以某种方式将图像目录复制到多个 Apache 服务器的问题。我并不是说这不可能做到……只是需要进行适当的规划。使用数据库作为图像存储将解决所有这些问题。
  • 使用数据库方法,WebObjects 将缓存您的图像,因此您实际上不必每次都访问数据库。
  • 如果您需要将图像与其他对象“关联”,我讨厌我的图像被存储在文件系统中。最终您很可能会遇到损坏的链接等问题……这会变得相当混乱。此外,如果您需要控制对图像的访问(例如,只有登录用户才能查看产品图像),您将需要依赖文件系统/Apache 安全性,这为您的应用程序增加了另一层复杂性。

再说一次,我知道很多人可能不同意这种方法。但是,它对我们非常有效,对于动态图像(或用户可以更改/上传的图像),我认为这是最有效的方法。也就是说,我们确实使用 Apache 提供静态图像。

我很想听听其他人和他们在将图像存储在数据库中的经历。您会听到很多人说“不要这样做,它不会运行良好”……但这些人真的尝试过吗?或者他们只是被告知不要这样做。我已经对这个主题很感兴趣一段时间了,并且我已经进行了广泛的搜索,但从未找到任何“正确”的答案。我认为这也取决于您使用的数据库以及数据库本身存储图像的方式。我知道有些数据库比其他数据库好得多,而我个人认为,这就是您最有可能遇到性能下降(如果有的话)的地方。

Geoff Hopson

[编辑 | 编辑源代码]

在 Fortnum & Mason 在线商店 http://www.fortnumandmason.com 上,产品目录包含很多图像。此外,他们 (F&M) 至少每年更改目录和相关图像两次。因此,我编写了一个工具,允许他们将产品图像上传到数据库中,仅仅是为了将所有内容放在一个地方以进行备份。当准备好部署新的目录时,将从数据库中提取图像并放置在 Web 服务器下(因为正如每个人都指出的那样,Web 服务器在分发图像方面特别擅长)。然后,主要的 F&M Web 应用程序将从 Web 服务器获取所有图像,而不是在从数据库获取后在 WebObjects 应用程序中进行缓存。

但是,在开发过程中,我们直接使用来自数据库的图像。命令行开关切换图像是否从 Web 服务器或数据库读取。

执行所有这些操作意味着内存占用更小,因为应用程序不会缓存图像,并且它还意味着我们可以对 Web 服务器执行一些巧妙的操作来稍微分散负载。

Chuck Hill 昨天在关于使用 Web 服务器的优缺点方面写了一些内容 - http://lists.apple.com/mhonarc/webobjects-dev/msg05564.html(使用“archives”、“archives”作为用户名/密码)。

Arturo Pérez

[编辑 | 编辑源代码]

我个人的意见和经验仅供参考,我两种方法都试过。我一直都在讨论这个问题,所以我决定把它写下来,放在一个地方。

数据库的优点

  • 非常容易。
  • 效果很好。
  • 与其他所有东西保持一致;也就是说,所有数据都来自数据库。

数据库的缺点

  • 如果你使用 BLOB(或等效物),则在 BLOB 周围会有一些调整问题。
  • 我不理解人们所说的“它解决了单点故障”。难道你只是将单点故障从文件系统转移到了数据库?当然,数据库提供了复制功能,但你也可以使用可集群的文件系统(如 Transarc 或 Andrew File System (AFS))来实现相同的目的。
  • 这“毫无意义”。它只是将一堆非关系数据存储在关系数据库中。感觉很脏 :-)
  • 如果你需要操作数据,这将变得非常困难。例如,如果它是文本,并且你想修复一个拼写错误,那将很麻烦。如果是图像,更新 EXIF 标题将是一项苦差事。使用 Perl 脚本处理目录中的文件要容易得多。

文件系统的优点

  • 它更具可扩展性。
  • 如果需要,操作图像更容易。特别是脱机操作。
  • 它更便宜。(磁盘在成本方面比数据库便宜,尤其是在你需要 DBA 的情况下)。

文件系统的缺点

  • 它稍微难以管理。你可能仍然需要一个数据库来跟踪图像。
  • 构建可以上传图像的内容管理系统更难。WebDAV 可能会解决这个问题。

好吧,这是我的两分钱。

Chuck Hill

[编辑 | 编辑源代码]

将它们存储在数据库中的一大问题是,EOF 会缓存数据,至少会缓存一段时间。对于负载很高的网站或内容很大的网站,这会很快消耗掉内存。

另一种方法是将它们存储在文件系统中,但不能直接提供给 Web 服务器。在数据库中保留一个对象,该对象引用文件系统中的数据。使用 Java 流将数据从请求移动到文件系统,然后从文件系统移动到响应。这避免了 EOF 开销,但允许应用程序控制访问。让 Web 服务器直接访问和分发图像等效率更高,但如果你有访问限制,这将不可行。这种混合数据库/文件系统方法在这种情况下很有用。

PetiteAbeille 写了一篇关于 EOF 文件系统适配器的文章,这篇文章可能与这个问题相关:http://www.wodeveloper.com/omniLists/eof/2002/June/msg00053.html

Tom Pelaia

[编辑 | 编辑源代码]

我们在 WebObjects 应用程序(电子航海日志)中从数据库获取图像。这是一个访问频率非常高的网站,允许用户创建包含文本、图像和其他附件的条目。我们发现了从数据库加载图像的其他“优点”。

数据库的其他优点

  • 航海日志不是唯一使用我们数据库的应用程序。我们只使用数据库的一部分。其他应用程序写入数据库,并向我们的航海日志添加条目,并使用我们的航海日志中的数据。将完整的航海日志条目(图像、文本、附件)存储在数据库中意味着其他应用程序可以轻松访问航海日志条目。因此,我们很好地集成了。
  • 数据库是所有数据的唯一官方来源
  • 备份一致且容易。备份数据库时,会备份所有航海日志数据,并且备份是自一致的。
  • 将数据从一台机器转移到另一台机器更容易,因为你只需要转移一项内容。
  • 由于图像由用户提供,因此数据库提供了一种避免文件名冲突的方法。如果你使用文件系统,你需要小心保存文件名以避免重复。
  • 我们几年前开发的第一个航海日志(没有 WO)将包括图像在内的数据存储在文件系统中。事实证明这非常不灵活。如果你想重新组织文件夹布局,许多链接将断开。对于数据库存储来说,这根本不是问题。
  • 数据库 simply 更灵活。文件系统存储非常严格。你可以将文件存储在当前应用程序中,但需求会随着时间的推移而发生变化,数据库存储在支持未来需求方面更加灵活,而这些需求你今天可能还没有考虑到。

选择数据库存储还是文件系统存储实际上取决于你的应用程序。对于我们的应用程序来说,电子航海日志正越来越多地与其他系统集成,而数据库在这项集成中变得至关重要。

我很乐意将图像存储在数据库中,但是...我的客户使用 Oracle 或 FrontBase。但是,我现在不幸的是,我正在参与一个必须使用 MS-SQL 的应用程序:似乎它真的不支持 BLOB(实际上,数据库管理员只是告诉我“不要在你的表中使用 BLOB,永远不要——我们对它们有最糟糕的体验”)。

我自己当然也尝试过 :)(使用测试数据库),发现确实存在一些问题,例如 BLOB 永远不会被 WHERE 子句找到(即使证明给出了正确的值)。我没有测试很长时间 :)

因此,虽然我坚信将图像存储在数据库中,但我可以理解其他不幸没有使用 FrontBase 的用户可能会有不同的意见 :)

我写这篇文章的原因是:在决定将图像存储在何处之前,请检查要使用的具体数据库。如果是 FrontBase 或 Oracle,你可能希望将它们存储在数据库中,如果是 MS-SQL,你可能希望将它们存储在文件系统中 :)

我现在有点生疏了,所以请原谅我在这方面有任何失误。WO 5.3 重新燃起了我对 WebObjects 的兴趣。

我已经对使用数据库中的图像进行了一些实验,并且通常发现,如果你做对了,与文件系统方法相比,这样做几乎没有性能影响——数据库在处理 blob 数据方面总体上有了很大改进,许多不存储图像的理由 simply 不再适用。此外,将图像存储在数据库中使编写、维护和备份应用程序变得容易得多。

通常,我会为分发图像创建直接操作方法。在你的 DirectAction 中,这将类似于以下内容(除了你可能希望添加验证,如果你不想让任何人通过篡改 URL 来获取你的图像):

 public WOActionResults imageAction() 
 {
   // PictureTest is an EOEntity with a BLOB containing the image data
   PictureTest pt = getPictureTestEO();
   return jpegResponseWithData(pt.image());    
 }   
 
 private PictureTest getPictureTestEO()
 {
   // Yes - you can get the session in a direct action
   //  you just need to be prepared to deal with one not existing
   // whether you return an image if no session exists depends on
  // on your own application needs.
   WOSession theSession =  existingSession();
   EOEditingContext ec = (theSession == null) ?  new EOEditingContext() : theSession.defaultEditingContext();
   String picid = (String)request().formValueForKey("picid");
   return (PictureTest)EOUtilities.objectMatchingKeyAndValue(ec, "PictureTest","id", new Integer(picid));
 }
 
 private WOResponse jpegResponseWithData(NSData theData)
 {
  // This method returns the data so that the browser
  // recognizes the image type. In this particular application
  // I've just hardcoded a mime type of JPEG because I only
  // use JPEG images, but a better way would be to store the mime-type
  // that corresponds to the image data in the BLOB as a separate
  // field. I might revise this sample later on to show that.
   WOResponse response = WOApplication.application().createResponseInContext(context());
   response.appendHeader("image/jpeg", "Content-Type");
   response.appendContentData(theData);
   return response;
 }

然后,在你的 WOComponent 中,你可以创建一个虚拟访问器,如下所示

 public String imageURL()
 {
   return context().directActionURLForActionNamed("icon", null) + "?picid=" + pictureItem.id();
 }

在本例中,pictureItem 是我在 WORepetition 中使用的 PictureItem EOEntity 实例——我只是从当前选择的图片中提取 ID 号。

然后,在 WOBuilder 中,将 WOImage 或 WOActiveImage 的 src 绑定绑定到 imageURL

这种方法消除了通过直接绑定到 EOEntity 的属性而获得的许多开销,并且实际上没有做太多额外的工作。

Bill Bumgarner

[编辑 | 编辑源代码]

将图像存储在数据库中通常不是一个好主意,原因很多。首先,它比直接从 Web 服务器提供图像要慢得多,并且它完全绕过了从文件系统提供图像时存在的许多自动“优化”。如果无法避免,那就无法避免……但是,如果你希望将你的解决方案扩展到一个庞大的用户群或高访问率,那么预计将花费大量的工程和硬件成本才能使数据库中的图像快速运行。

特别地

图像通常从文件系统中静态提供...因为你现在将它们作为动态内容提供,所以会产生以下性能影响

  • 没有客户端缓存 [糟糕] 页面上的单个图像的五个副本会在 WO 上产生五个单独的命中。
  • 图像请求必须序列化——不仅 IMAGE 命中必须序列化,而且 WOF 应用程序上的所有其他命中都必须等待任何挂起的图像命中处理完毕。就 Netscape 的 REALLY SLOW 表布局算法而言,该算法需要知道所有图像的大小,这意味着用户在 ALL 图像命中至少返回图像大小之前,将看不到表格的内容……由于命中是序列化的,这意味着除了最后一个图像之外的所有图像都必须被完全处理。
  • 静态命中与完全动态命中之间的性能差异非常大 [有利于静态命中]。想想看...静态命中基本上意味着 Web 服务器打开一个文件,将内容读/写到套接字,关闭...动态命中需要 IPC、数据库往返 [可能]、一堆内存操作、请求/响应传递等等等等...
  • 没有服务器端缓存;你的应用程序的每个实例最终都会在其内存中保存一个每个图像的副本。同样,数据库和 WO 应用程序服务器之间的 IPC 也必须来回传递所有这些数据。
  • 大多数数据库并不擅长处理 BLOB……无论如何

替代方法?

在你的数据库中放一个文件系统中的路径,而不是一个 blob;抽象地进行任意设置以方便管理等等...如果你真的需要图像来自数据库,那么构建一个图像管理器,该管理器维护文件系统中图像的层次结构,并仲裁数据库与图像之间的更新。

一个想法:如果需要刷新图像并且担心客户端或代理防火墙缓存,请在文件系统中重命名图像(或移动它)并生成一个新的 URL - 这应该是图像管理器的责任。

John Ours

[编辑 | 编辑源代码]

我同意上面所说的大部分内容,但让我经常将图像和文件移入数据库的一个考虑因素是安全性。如果没有安全考虑,那么将它们放在文件系统上并直接从 Web 服务器提供它们是相当合理的(您最大的挑战是保持一切同步...确保粗心的管理员或病毒扫描器不会来删除/重命名/更改文件从您的应用程序中)。但是,您经常会处理诸如“这组用户可以阅读此 PDF”或“只有特定级别的注册用户才能看到此图像”之类的用例。在这些情况下,您通过动态提供 PDF 或图像而遭受的打击是为尝试实现其他访问控制(例如,物理分离文件并使用 .htaccess 或 Windows 文件权限)而付出的管理头痛的小代价。以我的经验,大多数针对此问题的解决方案最终都会采用代理安排 - 应用程序读取资源然后将其写回客户端 - 在这种情况下,图像可能已被很好地保存在数据库中。

华夏公益教科书