我应该把上传的文件名存储在数据库中吗?

3
我有一个带有自增ID的数据库表作为主键。
对于该表的每条记录,我最多可以拥有3个文件,这些文件可以是公开的,因此不需要随机生成文件名,而且这些文件是可选的。
我认为有2种可能的解决方案:
  • 在3个可空的varchar列中存储随机生成的文件名,并将所有文件存储在同一位置:

    • columns: a | b | c
    • uploads/f6se54fse654.jpg
  • 不要存储文件名,但将它们放置在特定的文件夹中,并将它们命名为与主键值相同的名称:

    • uploads/a/1.jpg
    • uploads/b/1.jpg
    • uploads/c/1.jpg
使用这种最后一种解决方案,我知道uploads/a/1.jpg属于具有ID 1的记录,并且是类型a的文件。但我必须检查文件是否存在,因为文件是可选的。
您认为这样做有良好的实践吗?或者可能有更好的方法吗?

从技术上讲,两种方法都可以。然而,我建议将文件名存储在数据库中。这样,如果您必须移动文件、重新构造文件系统等,您只需编写一些 SQL 代码即可与新的文件位置匹配。 - Nic3500
如果文件被移动,那么数据库需要进行更新... - root
生成文件名的动机是什么?你提出了一个基础架构设计问题,但没有给足够的上下文信息。 - root
1
我认为上下文是清楚的,比如想象一个带有公共个人资料图片的用户,用户ID是唯一的,因此我可以将这个ID作为文件名。存储文件为“[ID].jpg”和在用户数据库记录中存储名称的好处是什么? - Marc
1个回答

4
如果你所说的文件是用于展示或下载给用户的(无论是给访客还是认证用户,是否根据角色(ACL)进行过滤),那么重要的是确保(在我看来)用户不能猜测除了已经发送给他的相关资源内容以外的其他信息。没有一种完美的解决方案适用于所有情况,因此让我们举个例子来给你更多解释。
为了增强敏感数据的安全性和总体不透明度,例如对于特定情况下的uploads/users/7/invoices/3.pdf,我认为明智的做法是确保绝对没有人能够猜测到与用户或任何其他实体可能相关联的文件数量(否则,在这个例子中,我们可以想象出可能有其他可访问的文件-1.pdf和2.pdf)。从设计上来说,我们通常希望在明确定义和特定的情况和背景下提供文件访问权限。然而,对于一个打算被所有人看到的图像文件(例如个人资料照片),情境在某种程度上是很重要的。
如果你选择将自动递增的标识符作为文件名称来引用你的文件,这也可以提供有关数据库中存储的数据大小的信息(/uploads/invoices/128.pdf 表示你的服务器上可能已经有 127 份发票),并且可能会激励不道德的人尝试获取不应该在定义的上下文之外获取的资源。如果你选择使用某种唯一生成的标识符(GUID),这种情况可能不太明显。
我建议您阅读本文,了解有关生成(G)/(U)UID(128位十六进制数字)并将其存储在数据库中以供每个上传或创建的文件使用。如果您使用MySQL的最新版本,则可以将此标识符托管在binary(16)类型中,该类型提供自动转换为UUID,我让您阅读这个有趣的主题相对应的内容。它可能会输出/uploads/invoices/b0016303-8e4f-487a-8c30-5dddf1ebf7e9.pdf,只要确保生成的标识符是唯一散列即可。在这里谈论性能问题似乎对我来说没有用,因为今天有许多缓存文件、路径和URL的方法,在很多情况下避免了每次调用资源时进行请求(通常按其在大数据案例中的受欢迎程度排序)。
最后,许多网络和移动平台应用程序(我想到了Slack、Discord、Facebook、Twitter等)每天存储许多媒体文件,这些文件通常与用户账户相关,包括公共和保密文件和信息,并为每个文件生成唯一的哈希值。
Twitter正在使用自己的唯一标识符字符串(64位BIGINT)生成器,称为Twitter Snowflake,您可能也会对其感兴趣。它基于UNIX纪元值,该值在每毫秒时刻都是唯一的。
并没有一个全局和完美的解决方案可以应用于所有情况,但我希望这能帮助您深入研究并找到适合于每个上下文和实体存储和链接文件的“最佳解决方案”。

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