11

我在由 1and1.com 托管的 Linux 服务器上收到了数千用户上传的数千张图片(我相信他们使用 CentOS,但不确定版本)。这是一个与语言无关的问题,但是,供您参考,我使用的是 PHP。

我的第一个想法是将它们全部转储到同一个目录中,但是,我记得不久前,可以在一个目录中删除多少个文件或目录是有限制的。

我的第二个想法是根据用户的电子邮件地址对目录内的文件进行分区(因为无论如何我都使用它作为用户名),但我不想遇到目录中目录的限制......

无论如何,对于来自 user@domain.com 的图像,我打算这样做:

/images/domain.com/user/images...

这是聪明的做法吗,如果成千上万的用户说“gmail”也许我可以更深入,像这样

/images/domain.com/[first letter of user name]/user/images...

所以对于 mike@gmail.com 它将是......

/images/domain.com/m/mike/images...

这是一个不好的方法吗?其他人都在做什么?我也不想遇到太多目录的问题......


有关的:

4

6 回答 6

28

我会做以下事情:

  1. 获取每个图像的 MD5 哈希值。
  2. 将 MD5 哈希写入您正在跟踪这些内容的数据库中。
  3. 将它们存储在一个目录结构中,您可以在其中使用 MD5 哈希十六进制字符串的前几个字节作为目录名称。因此,如果哈希是“abcdef1234567890”,您会将其存储为“a/b/abcdef1234567890”。

使用哈希还可以让您合并多次上传的同一张图片。

于 2009-05-23T00:28:50.453 回答
4

扩展 Joe Beda 的方法:

  • 数据库
  • 数据库
  • 数据库

如果您关心按用户、原始文件名、上传日期、拍照日期 (EXIF) 等对文件进行分组或查找,请将此元数据存储在数据库中并使用适当的查询来挑选适当的文件。

使用数据库主键——无论是文件哈希还是自动递增的数字——在一组固定的目录中定位文件(或者,使用每个目录的固定最大文件数 N,当你填满时,转到下一张,例如第k张照片应该存储在{somepath}/aaaaaa/bbbb.jpgaaaaaa = floor(k/N) 的位置,格式为十进制或十六进制,而 bbbb = mod(k,N),格式为十进制或十六进制。如果层次结构太平对你来说,使用类似的东西{somepath}/aa/bb/cc/dd/ee.jpg

不要将目录结构直接暴露给您的用户。如果他们使用 Web 浏览器通过 HTTP 访问您的服务器,请给他们一个类似 www.myserver.com/images/{primary key} 的 URL,并在 Content-Type 标头中编码正确的文件类型。

于 2009-05-23T00:38:54.053 回答
3

我用于另一个要求但可以满足您的需求的是使用简单的约定。

加 1 得到新号码的长度,然后加上这个号码的前缀。

例如:

假设“a”是一个用最后一个 id 设置的 var。

a = 564;
++a;
prefix = length(a);
id = prefix + a; // 3565

然后,您可以使用以下约定为目录使用时间戳:

20092305 (yyyymmdd)

然后你可以像这样爆炸你的路径:

2009/23/05/3565.jpg

(或者更多)

这很有趣,因为您可以同时按日期和数字保持排序顺序(有时很有用)并且您仍然可以在更多目录中分解您的路径

于 2009-05-23T00:28:37.863 回答
3

这是我为这种情况写的两个函数。它们已经在一个拥有数千名成员的网站上使用了一年多,每个成员都有很多文件。

本质上,这个想法是使用每个成员唯一的数据库 ID 的最后一位来计算一个目录结构,为每个人提供一个唯一的目录。使用最后一个数字,而不是第一个数字,可确保目录分布更均匀。每个成员都有一个单独的目录意味着维护任务要简单得多,而且你可以看到人们的东西在哪里(就像视觉上一样)。

// checks for member-directories & creates them if required
function member_dirs($user_id) {

    $user_id = sanitize_var($user_id);

    $last_pos = strlen($user_id);
    $dir_1_pos = $last_pos - 1;
    $dir_2_pos = $last_pos - 2;
    $dir_3_pos = $last_pos - 3;

    $dir_1 = substr($user_id, $dir_1_pos, $last_pos);
    $dir_2 = substr($user_id, $dir_2_pos, $last_pos);
    $dir_3 = substr($user_id, $dir_3_pos, $last_pos);

    $user_dir[0] = $GLOBALS['site_path'] . "files/members/" . $dir_1 . "/";
    $user_dir[1] = $user_dir[0] . $dir_2 . "/";
    $user_dir[2] = $user_dir[1] . $dir_3 . "/";
    $user_dir[3] = $user_dir[2] . $user_id . "/";
    $user_dir[4] = $user_dir[3] . "sml/";
    $user_dir[5] = $user_dir[3] . "lrg/";

    foreach ($user_dir as $this_dir) {
        if (!is_dir($this_dir)) { // directory doesn't exist
            if (!mkdir($this_dir, 0777)) { // attempt to make it with read, write, execute permissions
                return false; // bug out if it can't be created
            }
        }
    }

    // if we've got to here all directories exist or have been created so all good
    return true;

}

// accompanying function to above
function make_path_from_id($user_id) {

    $user_id = sanitize_var($user_id);

    $last_pos = strlen($user_id);
    $dir_1_pos = $last_pos - 1;
    $dir_2_pos = $last_pos - 2;
    $dir_3_pos = $last_pos - 3;

    $dir_1 = substr($user_id, $dir_1_pos, $last_pos);
    $dir_2 = substr($user_id, $dir_2_pos, $last_pos);
    $dir_3 = substr($user_id, $dir_3_pos, $last_pos);

    $user_path = "files/members/" . $dir_1 . "/" . $dir_2 . "/" . $dir_3 . "/" . $user_id . "/";
    return $user_path;

}

sanitize_var() 是用于清理输入并确保其为数字的支持函数,$GLOBALS['site_path'] 是服务器的绝对路径。希望,否则它们将是不言自明的。

于 2009-05-23T00:39:00.387 回答
2

Joe Beda 的回答几乎是完美的,但请注意,MD5 已被证明在笔记本电脑上的 iirc 2 小时内可碰撞?

也就是说,如果您真的会以上述方式使用文件的 MD5 哈希,您的服务将容易受到攻击。攻击会是什么样子?

  1. 黑客不喜欢某张照片
  2. 他确保这是您正在使用的普通 MD5(image+secret_string 的 MD5 会吓到他)
  3. 他使用了一种神奇的方法,将一张(在这里发挥你的想象力)哈希的图片与他不喜欢的照片相撞
  4. 他像往常一样上传照片
  5. 您的服务用新服务覆盖旧服务并显示两者

有人说:那我们不要覆盖它。然后,如果可以预测某人会上传某些东西(网络上的热门图片可能会被上传),那么就可以先获取它的“散列位置”。用户在上传小猫的图片时会很高兴,他会发现它实际上看起来像(在这里发挥你的想象力)。我说:使用 SHA1,因为它在 iirc 127 年中被 10.000 台计算机集群证明是可破解的?

于 2009-05-23T07:33:54.173 回答
0

在这方面可能会迟到。但是一种解决方案(如果它适合您的用例)可能是文件名散列。这是一种使用文件名创建易于重现的文件路径的方法,同时还创建了一个分布良好的目录结构。例如,您可以使用文件名哈希码的字节作为其路径:

String fileName = "cat.gif";
int hash = fileName.hashCode();
int mask = 255;
int firstDir = hash & mask;
int secondDir = (hash >> 8) & mask;

这将导致路径为:

/172/029/cat.gif

然后,您可以cat.gif通过重现算法在目录结构中找到。

使用 HEX 作为目录名称就像转换int值一样简单:

String path = new StringBuilder(File.separator)
        .append(String.format("%02x", firstDir))
        .append(File.separator)
        .append(String.format("%02x", secondDir)
        .toString();

导致:

/AC/1D/cat.gif

几年前我写了一篇关于这个的文章,最近把它移到了 Medium。它有更多细节和一些示例代码:文件名哈希:创建哈希目录结构。希望这可以帮助!

于 2017-06-25T14:30:10.117 回答