存储大量图像

Posted

技术标签:

【中文标题】存储大量图像【英文标题】:Storing a large number of images 【发布时间】:2009-01-15 10:57:43 【问题描述】:

我正在考虑开发我自己的基于 php 的图库来存储大量图片,可能有数万张。

在数据库中,我将指向图像的 url,但问题是:我知道将它们全部放在服务器中的同一目录中是不切实际的,因为它会减慢爬网的访问速度,所以,您将如何存储所有这些?某种基于 jpeg/png 名称的树?

你会推荐我什么分割图像的规则?

(它将专注于在cheapo dot coms中使用,因此不可能对服务器进行修改)

【问题讨论】:

【参考方案1】:

过去我们也遇到过类似的问题。并找到了一个不错的解决方案:

为每张图片提供一个独特的指南。 为每个图像创建一个数据库记录,其中包含子图像的名称、位置、guid 和可能的位置(缩略图、缩小尺寸等)。 使用 guid 的前(一个或两个)字符来确定***文件夹。 如果文件夹中的文件过多,请重新拆分。更新参考资料,您就可以开始了。 如果文件数量和访问量过多,您可以将文件夹分散到不同的文件服务器上。

我们已经体验过,使用 guid,您可以获得或多或少统一的划分。它就像一个魅力。

可能有助于生成唯一 ID 的链接:

http://en.wikipedia.org/wiki/Universally_Unique_Identifier http://en.wikipedia.org/wiki/Sha1

【讨论】:

由于性能的原因,数据库调用通常非常昂贵,尤其是对于像图像这样的二进制数据。 更不用说从数据库中提供图像意味着您几乎总是发送数据,就好像您可以从文件系统提供服务一样,您可以让浏览器/服务器处理图像的缓存 @Gamecat 恕我直言,比生成 UUID 更好的是简单地散列文件名并将其开头用作目录名。这样你就不需要数据库,因为你总是可以重新计算哈希,这比访问数据库要快得多。 (我看到你提到了 SHA-1,但没有明确建议)。 @maaartinus,你可能是对的。但是我们已经有一个数据库(用于 CMS),我们只需要链接图片就可以了,这对我们来说非常有用。 如果你有一个整数唯一ID,一个简单的方法就是把它分成三个级别:xxx/yyy/filename.jpg。这样您就可以使用唯一 ID。例如,如果 id 是 100789,它将被存储为 100/789/filename.jpg。然后,每个级别最多有 1,000 个目录。总共有 1,000,000 个文件。而且,您可以根据分辨率拥有多个文件名:thumbnail.jpg、small.jpg 等。【参考方案2】:

几年前我从事电子文档管理系统的工作,我们几乎按照 Gamecat 和 wic 的建议做了。

也就是说,为每个图像分配一个唯一的 ID,并使用它来导出图像文件的相对路径。我们使用的MOD和wic建议的类似,但是我们每层允许1024个文件夹/文件,有3个层级,所以我们可以支持1G的文件。

但是,我们从文件中删除了扩展名。数据库记录包含 MIME 类型,因此不需要扩展。

我不建议将完整的 URL 存储在数据库记录中,只存储图像 ID。如果您存储 URL,您将无法在不转换数据库的情况下移动或重组您的存储。相对 URL 可以,因为这样至少可以移动图像存储库,但如果您只存储 ID 并派生 URL,您将获得更大的灵活性。

另外,我不建议允许从网络直接引用您的图像文件。而是提供服务器端程序(例如 Java Servlet)的 URL,并在 URL 查询 (http://url.com/GetImage?imageID=1234) 中提供图像 ID。

servlet 可以使用该 ID 来查找 DB 记录、确定 MIME 类型、导出实际位置、检查安全限制、日志记录等。

【讨论】:

优点。 servlet 请求是否仍然允许缓存?我正在寻找一个类似的问题,但在我的应用程序中,传输时间很关键,所以我一直在寻找在客户端缓存图像的方法。我在做梦吗? @MikeJ:您可以创建一个单独的类来访问图像。该类将知道如何从 id 派生路径等。它还可以包含一个缓存,可能作为您自己管理的哈希表,或者可能是一个罐装缓存类。 Servlet 将从该对象而不是磁盘中获取图像。【参考方案3】:

我通常只使用数字数据库 id (auto_increment),然后使用模数 (%) 运算符来确定文件的放置位置。简单且可扩展。例如,id 为 12345 的图像的路径可以这样创建:

12345 % 100 = 45
12345 % 1000 = 345

结束于:

/home/joe/images/345/45/12345.png

或者类似的东西。

如果您使用的是 Linux 和 ext3 以及文件系统,您必须注意目录中可以拥有的目录和文件的数量是有限制的。目录的限制是 32000,因此您应该始终努力保持目录数量较少。

【讨论】:

同时拥有 '345' 和 '45' 的目的是什么?似乎您的每个一级目录(如“345”)都只有一个子目录(在本例中为“45”)。【参考方案4】:

我知道将它们全部放在服务器中的同一目录中是不切实际的,因为它会减慢爬网的访问速度。

这是一个假设。

我设计的系统将数百万个文件平放在一个目录中,效果很好。它也是最容易编程的系统。大多数服务器文件系统都毫无问题地支持这一点(尽管您必须检查您使用的是哪一个)。

http://www.databasesandlife.com/flat-directories/

【讨论】:

感谢分享。 OP提到了PHP,一个实际问题是FTP访问包含大量文件的目录可能会超时。 我认为重要的是,正如您在博客文章中所做的那样,一些 文件系统支持单个文件夹中的大量文件。根据我的经验,一些(其他)文件系统在其规定的大型文件规范之外工作,但并非所有文件操作都可以工作。如果您要在一个文件夹中存储大量文件,请先进行测试!也就是说,为什么不使用某种哈希值对文件夹结构进行树平衡?【参考方案5】:

在保存与 auto_increment id 关联的文件时,我使用类似以下的内容,它创建三个目录级别,每个级别由 1000 个目录组成,每个第三级目录中有 100 个文件。这支持约 1000 亿个文件。

如果 $id = 99532455444 那么以下返回 /995/324/554/44

function getFileDirectory($id) 
    $level1 = ($id / 100000000) % 100000000;
    $level2 = (($id - $level1 * 100000000) / 100000) % 100000;
    $level3 = (($id - ($level1 * 100000000) - ($level2 * 100000)) / 100) % 1000;
    $file   = $id - (($level1 * 100000000) + ($level2 * 100000) + ($level3 * 100));

    return '/' . sprintf("%03d", $level1)
         . '/' . sprintf("%03d", $level2)
         . '/' . sprintf("%03d", $level3)
         . '/' . $file;

【讨论】:

【参考方案6】:

查看 XFS 文件系统。它支持无限数量的文件,Linux 支持它。 http://oss.sgi.com/projects/xfs/papers/xfs_usenix/index.html

【讨论】:

【参考方案7】:

您可以在表格中有一个 DateTime 列,然后将它们存储在以添加到表格中的图像的月、年甚至月、日、年命名的文件夹中。

例子

    2009 -01 --01 --02 --03 --31

这样你最终的文件夹深度不会超过 3 个。

【讨论】:

【参考方案8】:

我目前正面临这个问题,而 Isaac 所写的内容让我对这个想法产生了兴趣。我的功能有点不同。

function _getFilePath($id) 
    $id = sprintf("%06d", $id);
    $level = array();
    for($lvl = 3; $lvl >= 1; $lvl--)
        $level[$lvl] = substr($id, (($lvl*2)-2), 2);
    return implode('/', array_reverse($level)).'.jpg';

我的图片只有数千个,所以我最多只有 999999 个限制,所以它会将其拆分为 99/99/99.jpg 或 43524 拆分为 04/35/24.jpg

【讨论】:

【参考方案9】:

使用文件系统的层次结构。使用 001/002/003/004.jpg 之类的东西来识别您的图像会非常有帮助。不过,分区是另一回事。可能是随机的、基于内容的、基于创建日期的等等。这实际上取决于您的应用程序是什么。

【讨论】:

【参考方案10】:

您可以查看 Apple iPod 用于存储其多媒体内容的策略。有一层深度的文件夹和具有相同宽度标题的文件。我相信 Apple 人员在测试他们的解决方案上投入了大量时间,因此它可能会给您带来立竿见影的好处。

【讨论】:

我不太清楚你在这里的意思。能举个例子吗?【参考方案11】:

如果您处理的图片是数码照片,您可以使用 EXIF 数据对它们进行排序,例如按拍摄日期。

【讨论】:

【参考方案12】:

您可以将图像作为 blob 存储在数据库中(varbinary 用于 mssql)。这样您就不必担心存储或目录结构。唯一的缺点是您不能轻松浏览文件,但无论如何这在平衡的目录树中会很困难。

【讨论】:

IMO 这是一个糟糕的建议。 1.你的数据库很快就会变得庞大,它会带来其他问题。 2. 另一方面,无法使用缓存代理服务器(如 nginx 或 HAproxy)来缓存图像,这对于静态内容来说非常快。 3. DB将成为负载非常低的瓶颈。

以上是关于存储大量图像的主要内容,如果未能解决你的问题,请参考以下文章

如何创建矩阵向量来存储大量图像?

如何存储大量图像以使用 html 检索 [关闭]

在不超过 inode 或硬盘空间的情况下存储大量 jpeg 图像(Python)

在设备上显示和存储图像?

如何将图像动态存储在文件系统中?

将多个图像存储到 Firebase 并获取 url