高效地将用户上传的图片存储在文件系统中。

20

情境

用户可以发布一篇帖子,并在帖子中包含最多5张图片,每张上传的图片都需要重新采样和调整大小 - 共创建4个额外的图像。这意味着如果用户上传了5张图片,则总共要存储25张图片。

假设条件

  • 这些图片经过了适当的检查,是有效的图像文件
  • 系统必须进行扩展(让我们假设首先有1000条帖子,因此最多5000个图像)
  • 每个图像都会被重命名为与数据库帖子条目的auto_increment id相关的名称,并包括相关后缀,例如12345_1_1.jpg 12345_2_1.jpg - 因此没有重复问题
  • 这些图像并不涉及敏感内容,因此可以直接访问它们(尽管目录列表将被禁用)

可能的方法

  • 由于id是唯一的,因此我们只需将它们放入一个文件夹中即可(在某一点之后效率低下)。
  • 可以为每篇帖子创建一个文件夹,并将所有图像放入其中,例如ROOT/images/12345(同样,将最终导致大量的文件夹)
  • 可以基于日期进行图像存储,即每天创建一个新文件夹,并将当天的图像存储在其中。
  • 可以根据调整大小的类型存储图像,例如所有原始文件可以存储在一个文件夹中images / orig,所有缩略图可以存储在images / thumb中(我认为Gumtree使用了这种方法)。
  • 可以允许在创建另一个文件夹之前存储X个文件。

谁有关于可扩展存储图像的最佳实践/方法的经验?

注意:我预先知道有人会提到S3 - 让我们暂时假设我们想保留本地图像。

感谢您的阅读。


1
将所有图像放在一个文件夹中是否“低效”取决于所使用的文件系统。在btrFS或Reiserfs上,目录中的项目数量对查找时间无关紧要。请查阅您特定文件系统的文档。 - Johan
3
在同一文件夹中有大量图像可能很麻烦,特别是如果您想在(S)FTP上列出/备份内容。 - Markus Hedlund
当我声称“低效”时,我可能应该更加小心——这与文件系统提供图像的能力无关(这是它们的工作,实际上它们非常出色),而更多地涉及将5000个图像存储在一个文件夹中所带来的固有开销,以及某些应用程序尝试列出/加载它们时可能会崩溃(或需要一整天才能完成)。 - Rarriety
什么应用程序?在哪个系统上?若是在一个目录中列出5000个文件都让你感到困难,那也太小儿科了吧!!! - Manse
3个回答

13

我们有一个系统正在大规模生产中,目前已经有30,000多个文件和20+ GB的数据量...

   Column    |            Type             |                        Modifiers                         
-------------+-----------------------------+----------------------------------------------------------
 File_ID     | integer                     | not null default nextval('"ACRM"."File_pseq"'::regclass)
 CreateDate  | timestamp(6) with time zone | not null default now()
 FileName    | character varying(255)      | not null default NULL::character varying
 ContentType | character varying(128)      | not null default NULL::character varying
 Size        | integer                     | not null
 Hash        | character varying(40)       | not null
Indexes:
    "File_pkey" PRIMARY KEY, btree ("File_ID")

文件只存储在一个目录中,以整数File_ID作为文件名。我们已经有超过30,000个文件没有问题。我测试过更多的文件也没有问题。 这是在使用RHEL 5 x86_64和ext3文件系统。 我会再次这样做吗?不会。让我分享一些重新设计的想法。
1. 数据库仍然是文件信息的“主要来源”。 2. 每个文件都被sha1()哈希并存储在基于该哈希的文件系统层次结构中: /FileData/ab/cd/abcd4548293827394723984723432987.jpg 3. 数据库在每个文件上存储元信息时更加智能。它将成为一个三个表的系统: File:存储诸如名称、日期、IP、所有者和Blob(sha1)指针等信息。 File_Meta:根据文件类型存储文件的键/值对。这可能包括像Image_Width等信息。 Blob:存储对sha1的引用及其大小。
这个系统通过存储由哈希引用的数据来去重文件内容(多个文件可以引用相同的文件数据)。使用rsync备份同步文件数据库非常容易。 此外,消除了给定目录包含大量文件的限制。
文件扩展名将作为唯一文件哈希的一部分存储。例如,如果一个空文件的哈希值为abcd8765...那么一个空的.txt文件和一个空的.php文件将引用相同的哈希值。然而,它们应该分别引用abcd8765.phpabcd8765.txt。为什么呢?
Apache等可以根据文件扩展名自动选择内容类型和缓存规则。因此,以反映文件内容的有效名称和扩展名存储文件非常重要。
你看,通过委派文件交付,这个系统可以通过nginx真正提高性能。详见http://wiki.nginx.org/XSendfile
我希望这样有所帮助。注意身体。

1
点赞真正优雅的架构。我正在重构我的静态图像服务器,使用你的想法。谢谢! - Jens Roland
只是为了澄清您提出的数据库结构;将文件和Blob分开可以在相同文件(blob)的情况下节省40字节的哈希字段,但还有其他原因吗? - Jens Roland
其次,由于文件和文件元数据具有1对1的关系,我假设您只是将它们分开以使文件表尽可能精简?我的意思是,随着时间的推移,文件元数据表可能会变得非常庞大,因此确保您可以访问基本的文件属性而不会增加额外负担似乎是有意义的。但是,是否还有其他原因导致了这种分离呢?如果您想要进行特定于文件类型的数据,您会如何构建结构? - Jens Roland
哇,真的是太棒了。不过我有一个问题,你说“Blob”存储了“对sha1的引用以及其大小”,这是什么意思? - Madara's Ghost
@JensRoland - 我认为File和File_Meta之间存在一对多的关系,每个File_Meta行仅存储一个键/值对(类似于EAV)。 - Brilliand

1
我会将所有的图片存储在一个单独的文件夹中 - 数据库会跟踪文件名 - 保持简单。

0
首先,我建议为图像创建一个表格。这是一个包含一行和一个图像文件的表格:
| id  | filename | type     | storage |
---------------------------------------
| 123 | 123.png  | original | store1  |
  • id 是自增的整数或其他同样唯一的标识。
  • filename 是文件基本名称。这使您可以移动文件并仅更新代码。文件名可以是{file_id}.{extension}
  • type 是图像类型:originalthumbnailresized等等。也可以是尺寸:100x100500xx500(其中500x将是无限高度,x500将是无限宽度)。这些只是一些示例。
  • storage 是文件所在位置的标识符,可以是目录。假设您将图像存储在post_images中,文件名为123.png,存储为store1,则路径将为post_images/store1/123.png

我还没有尝试过这个方法,但我有问题,因为Web应用程序在同一个目录中存储10k+个文件。


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