上传图片的最佳存储位置是SQL数据库还是磁盘文件系统?

172

我正在开发一个应用程序,允许用户将图片上传到服务器。每天预计会有大约20张jpeg格式的图片上传,并且可能不会被编辑/调整大小。(这是另一个问题,在存储之前如何在服务器端调整图像大小。也许有人可以在评论中提供 .NET 的资源)。

  • 将图片作为文件存储在文件系统中,并创建一个带有该图像准确路径的表记录。

  • 或者,使用数据库服务器的“image”或“binary data”数据类型将图像本身存储在表中。

我认为两种方法都有优缺点。 我喜欢a)的原因是我可以轻松地重定位文件并只需更改表项。另一方面,我不喜欢在web服务器上存储业务数据,也不想将web服务器连接到保存业务数据的任何其他数据源(出于安全考虑); 我喜欢b)的原因是所有信息都在一个地方,并且可以通过查询轻松访问。另一方面,数据库很快就会变得非常庞大。外包该数据可能更加困难。


2
我没找到它,它在哪里? - Tobias
6
这里是一个问题链接:https://dev59.com/OnVD5IYBdhLWcg3wXaYd,询问是否应该将图像存储在数据库中。 - jason saldo
可能是重复的问题:将图像存储在数据库中-是还是不是? - Liam
18个回答

113

通常情况下,我会将文件存储在文件系统中,因为那是它的本职工作,但也有例外情况。对于文件来说,文件系统通常是最灵活和性能最好的解决方案。

把文件存储在数据库中存在一些问题——文件通常比平均行要大得多,包含许多大文件的结果集将消耗大量内存。此外,如果您使用的存储引擎对写入执行表锁(例如ISAM),则根据您在那里存储的文件大小/速率,您的文件表可能经常被锁定。

关于安全性——我通常会将文件存储在文档根目录之外的目录中(无法通过http请求访问),并通过一个检查适当授权的脚本进行服务。


8
请问您能否请您解释一下最后一段(关于安全性)的技术细节,或者提供任何指引都会非常有帮助。谢谢。 - VishwaKumar
51
如果你将网站根目录配置为"public"文件夹(比如my_website/public/而不是只有my_website/),那么你可以将图片和应用程序的其他内容一起存储在my_website/my_images文件夹中。此时,你的img标签将引用"my_website/image.php?img_id=55"而不是"my_website/avatar.png",而你的image.php脚本会在验证你的凭据并解析你传递的id后返回实际的图像。这样,只有正确登录的用户才能查看该图像。 - Captain Hypertext
13
嘿,队长,你应该将那个变成一个真正的答案,这样你就可以得到积分$$$。 - Andrew
5
请在安全性/防止文件破坏您的网站方面添加更多说明。 - Andrew
1
那不会扩展,文件夹中的文件数量有限制,如果您计划将文件分成多个文件夹,则会增加索引文件的复杂性(以确定文件实际存储位置)。此外,搜索速度会非常慢。 - Hardik
显示剩余2条评论

55
选项B唯一的好处是将所有数据放在一个系统中,但这是一个虚假的好处!你可能会争辩说你的代码也是一种数据,因此也可以存储在数据库中 - 你喜欢吗?
除非你有一些独特的情况:
- 业务逻辑应该放在代码中。 - 结构化数据应该放在数据库中(关系型或非关系型)。 - 大量数据应该放在存储中(文件系统或其他)。

Files, Code, Data

不必使用文件系统来存储文件。相反,您可以使用云存储(例如Amazon S3)或基于其上的基础设施即服务(例如Uploadcare):

https://uploadcare.com/upload-api-cloud-storage-and-cdn/

但将文件存储在数据库中是一个不好的主意。


32
我知道这是一个旧帖子。但是许多访问此页面的访客与问题无关。尤其对于新手来说。
如何在我们的网站上上传和存储图像或文件:
对于静态网站来说,可能没有问题,因为某些共享主机的文件存储仍然足够。问题出现在动态网站变得更大时。更大的数据库可以处理,但更多的文件数量,比如图像,就成了问题。网站中有两种类型的图像:
1. 来自动态博客管理员的图像。通常,在上传之前会对这些图像进行优化。 2. 用户上传的图像。如果允许用户上传图像,例如头像,或者用户可以创建博客内容并从文本编辑器中插入一些图像。在这种类型的图像中,很难预测大小。用户可以上传大图像,只是为了调整视图大小而不是实际图像大小。
忽略上述第一项,如果我们的网站没有图像优化功能,那么可以通过以下提示临时解决第二项问题:
  1. 不要允许用户直接从文本编辑器上传内容,而是将他们重定向到图库。在该页面上,用户必须先上传文件,然后才能将其嵌入到内容中。这种方法称为文件管理器。

  2. 为用户提供裁剪图像功能来上传图片。这将限制图像的大小,即使用户上传了非常大的文件。最终图像将是裁剪后的结果。我们可以在服务器端定义图像大小,例如只接受500KB或更低的文件。

现在,这仅仅是一个临时的解决方案。对于最终的解决方案,问题如下:

  • 如何处理大型图像存储?
  • 调整尺寸或更改扩展名。
  • 大型或中型网站或电子商务如何处理图像文件存储?

那么我们可以做以下几点:

  1. 从共享主机VPS迁移到独立主机。还不够?那就升级到专用主机。

  2. 为文件存储创建自己的服务器。搜索如何操作。这并没有你想象的那么困难。有些人为他们的网站这样做。

  3. 简单的方法是使用CDN文件存储服务。

好的,1和2有点贵。但是3不贵。我认为这是最好的解决方案。
一些CDN服务允许您存储任意数量的网页文件。
问题是,“如何从我们的网站上传文件到CDN?”
别担心,一旦您注册,通常是免费的,您将获得如何上传文件并获取它们在您的网站上的链接的指导。您还将获得API等更多功能。很容易。
一些供应商提供14天的免费服务,但存储空间和带宽有限。但对于起步来说,这也足够了。唯一的问题是“人们从未尝试”。
希望对新手有所帮助。

15
我们曾经多次有客户坚持使用选项B(数据库存储),并在几个不同的后端上进行尝试,但最终我们总是回到了选项A(文件系统存储)。即使在我们最后一次尝试的SQL Server 2005中,也不能很好地处理这样大的BLOB。具体而言,我们遇到了严重的膨胀和锁定问题。
另外需要注意的是,如果您正在使用基于NTFS的存储(Windows服务器等),您可能需要考虑找到一个方法来避免将成千上万个文件放入一个目录中。我不确定为什么,但有时文件系统无法很好地处理这种情况。如果有人想更多地了解这方面的知识,我会很乐意听取他们的意见。
但我总是尝试使用子目录来分散一些东西。创建日期通常很适合这个目的:
Images/2008/12/17/.jpg
......这提供了一个不错的分离级别,并在调试期间有所帮助。当有真正巨大的目录时,资源管理器和FTP客户端可能会有点吃力。
编辑:2017年的一个快速注释,在SQL Server的较新版本中,有新的选项可以处理大量的BLOBs,可以避免我讨论的缺点。
编辑:2020年的一个快速注释,AWS/Azure等中的Blob存储也是多年来的一个选择。对于许多基于Web的项目来说,这非常适合,因为它便宜,并且在必要时还可以简化部署、扩展到多个服务器、调试其他环境等问题。

4
关于在同一目录下存放过多文件的警告很重要。这可能会导致在生产环境中难以发现的错误。 - digao_mb
1
我之前遇到过这个问题。当一个文件夹中有大约10,000个文件时,NTFS的行为变得不可预测。 - Faiz
2
不仅是NTFS,BTRFS也存在在一个文件夹中处理大量图像的问题。换句话说,如果您尝试使用“ls”,它将需要很长时间(挂起)。或者删除。 - sunapi386

12

我最近创建了一个PHP/MySQL应用程序,将PDF和Word文件存储在MySQL表中(每个文件大约40MB)。

优点:

  • 上传的文件会与其他所有内容一起复制到备份服务器上,不需要单独的备份策略(安心)。
  • 设置Web服务器稍微简单一些,因为我不需要拥有一个uploads/文件夹并告诉所有应用程序它在哪里。
  • 我可以使用事务来编辑以提高数据完整性 - 我不必担心孤立和丢失的文件

缺点:

  • 由于其中一个表中有500MB的文件数据,所以mysqldump现在需要很长时间。
  • 与文件系统相比,总体上不太内存/CPU有效。

我认为我的实现是成功的,它满足备份要求并简化了项目的布局。对于使用该应用程序的20-30人而言,性能也可以接受。


10

一定要调整图像大小,如果可以的话,请检查其格式。曾经有过上传和服务于无意识主机的恶意文件的情况。例如,GIFAR漏洞允许您在GIF文件中隐藏恶意Java小程序,然后能够读取当前上下文中的Cookie并将其发送到另一个站点以进行跨站脚本攻击。通常调整图像大小可以防止这种攻击,因为它会破坏已嵌入的代码。虽然此漏洞已被JVM修补程序修复,但不加清理地简单地提供二进制文件会使你暴露于各种漏洞之中。

记住,大多数病毒扫描器只能针对文件系统运行-如果您将二进制文件存储在数据库中,则很难对其运行扫描器。


9

这基本上是我的工作内容。

  1. 将上传的图像存储在临时目录或内存中。
  2. 在永久存储之前处理该图像。 2.1. 颜色校正 2.2. 压缩 2.3. 根据图像尺寸创建多个副本 2.4. 重命名为.xl、.lg、.md、.sm等后缀
  3. 将所有处理过的图像文件(来自单个文件)打包到一个文件夹中,文件夹名称为 id,并将其与存储在数据库中的任何行/文档一起存储 image file name(或可能是随机名称作为图像名称)。
  4. 如果不存在,则创建 yyyy/mm/d path 文件夹。例如,2016/08/21。记住路径并将其存储在相同的文档和行中的数据库中。
  5. 将图像 id 文件夹移动到 path 文件夹中。(路径文件夹可以位于 /var/web-content 文件夹中。)
  6. 清空内存缓冲区或删除临时文件。

当您需要访问文档中提到的任何图像时,您具有包含图像的文件夹的路径和 id。例如 /var/web-content/{{path}}/{{id}}/image-file-name.sm.jpg

这样如果您需要删除所有处理过的图像文件,只需递归删除该文件夹及其内容。


7
我在我的网站上使用上传的图片,我肯定会选择选项a)。
另外,我强烈建议立即更改文件名,从用户所命名的照片更改为更易管理的名称。例如,使用日期和时间来唯一标识每张图片。
此外,将用户的文件名中的任何奇怪字符删除有助于避免未来的复杂情况。

5

SQL Server 2008有一种混合方法,称为filestream数据类型,在RunAs Radio #74上讨论过,它可以说是两全其美。虽然大多数人没有使用2008版本的选项,但如果你有这个选项,那么这个选项看起来非常酷。


3

大多数实现都是选项A。

选择选项B时,当你将这些位从数据库转换成可以在浏览器上显示的内容时,你会遇到一些困难。此外,如果数据库宕机,图片将无法使用。

我认为空间不是太大的问题...... 现在,1TB的驱动器只需要几百美元。

我们正在采用选项A,因为我们没有时间或资源来进行选项B。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接