平衡文件分发结构

时间:2012-08-30 09:20:13

标签: php file file-upload directory

我有一台服务器,可以上传和存储存档文件。为了提高列表和检查文件存在的性能,我需要在面向性能的目录结构中组织文件。

我有一个数据库表,它将保存文件的真实名称及其临时名称。

###################################
FILES
###################################
id int auto_increment primary key,
name varchar (255),
temp_name varchar (255)

根目录最多可包含1000个子目录,范围为0-999。每个目录将包含1000个文件。

所以结果将是

root/0 ==> will hold file having the id range from 1-999
root/1 ==> will hold file having the id range from 1000-1999
root/2 ==> will hold file having the id range from 2000-1999
.
.
.
root/999 ==> will hold file having the id range from 999,000-999,999

可以使用以下等式

找到存储文件的目录
$directory = floor($file_id_from_db/1000);

WHERE $ file_id_from_db从文件数据库表files中恢复。id

问题发生在第1,000,000个文件被上传时,我必须开始将文件存储在第二级。

我必须在第0个目录中创建第二级目录,范围从0到999 root / 0/0 - root / 0/999

然后当我到达root / 0/999并且我已经放入了1000个文件时,我需要移动到 root / 1/9999等等,直到我达到root / 999/999。

我当前的功能看起来像那样

function getPath($id){    
     $result = floor($file_id/1000);
     //Second level checks (Tried and crashed and burned)
     return "/$result"; 
}

我不知道如何实现创建子目录的逻辑?

感谢您的任何建议。

4 个答案:

答案 0 :(得分:3)

如果可以采用其他方法,您可以尝试以下方法:

  • 将db-id填充到例如20个地方(长远来说就足够了),如:00000000000001665765
  • 将该字符串拆分为suitalbe lenghts(例如2)并重新加入你的DIRECTORY_SEPARATOR

这将是您的存储路径,例如:

$id = 1665765;
$paddedId = str_pad($id, 20, '0', STR_PAD_LEFT);

echo $path = '/' . implode(DIRECTORY_SEPARATOR, str_split($paddedId, 2));

// ==> /00/00/00/00/00/00/01/66/57/65
这样,每个目录最多只能有100个目录/文件。 (如果你选择另一个分裂长度

,这当然会有所不同

要轻松创建目录,您可以使用mkdir的第三个参数。

mkdir(dirname($path), 0755, true);

答案 1 :(得分:1)

我正在使用它,就像一个魅力。

$dir = str_pad(substr( ($file_id + 1000), 0, (strlen(($file_id + 1000)) - 3)), 5, "0", STR_PAD_LEFT);

所有文件夹都有5位数字,如下所示:00001

这样就可以确保2在1而不是10之后等等。

简单有效。

添加:如果您想从0开始,则不会像这样使用它。 (注意-1)

$dir = str_pad(substr( ($file_id + 1000), 0, (strlen(($file_id + 1000)) - 3)) - 1, 5, "0", STR_PAD_LEFT);

另外一个补充:我想我得到了你想要的东西。

试试这个:一旦达到100万,它就会在现有文件夹中添加子文件夹。

$dir = str_pad(substr( ($file_id + 1000), 0, (strlen(($file_id + 1000)) - 3)) - 1, 4, "0", STR_PAD_LEFT);
if($dir > 1000) { $dir = str_pad(substr($dir, 0, strlen($dir) - 3), 4, "0", STR_PAD_RIGHT) . DIRECTORY_SEPARATOR . str_pad(substr($dir, -3) - 1, 4, "0", STR_PAD_LEFT); }

答案 2 :(得分:1)

愿这个功能更适合你吗?

function getPath($id)
{
    if ($id < 100) return "0".DIRECTORY_SEPARATOR;
    $id = str_pad($id,strlen($id)+(3-strlen($id)%3),"0",STR_PAD_LEFT);
    $in = array_map(create_function('$x','return ($x >= 1) ? ltrim($x,\'0\') : "0";'),str_split($id,3));
    array_pop($in);
    $in = array_reverse($in);
    return rtrim(implode(DIRECTORY_SEPARATOR,$in),DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR;
}

测试垫:http://codepad.org/alRDCWlU

答案 3 :(得分:0)

换句话说,您需要散列(或以其他方式详述)整数(id),以便它可以均匀地分布在目标地址空间。

一种方式是你正在采用的方式。假设您希望保留最多一千亿条记录,因此您管理的最大数量为999,999,999,999或四个级别。

function storeFile($id, $source_path)
{
    GLOBAL $BASE_PATH;
    $seq = sprintf("%012d", $id); // Transforms 42 in 000000000042

    $dir = str_split($seq, 3);  // Transforms in { 000 000 000 042 }

    $file = array_pop($dir);    // Get 042 which is the file name

创建后,我们需要创建中间目录(如果需要)编辑:实际上@Yoshi的解决方案要好得多)

    // Now build directory

    $path = $BASE_PATH;
    foreach($dir as $component)
    {
        $path .= "/$component";
        if (!is_dir($path))
            mkdir($path);
    }
    // The above can be replaced with mkdir(getPath($id), 0755, true); [@Yoshi]

    // Now $path exists and is /my/base/path/000/000/000

    // $path/$file is my file name, 042 in $path
    rename($source_path, $dest_path = "$path/$file");

    // Just to check. TRUE if everything was hunky dory; FALSE if something went bad.
    return file_exists($dest_path);

}

每次存储文件时,运行上述内容并根据需要创建目录。

恢复时,您可以运行

  function getPath($id)
  {
      GLOBAL $BASE_PATH;
      return $BASE_PATH . implode('/', str_split(sprintf("%012d", $id), 3));
  }

并直接获取文件名。

UPDATE :'/'是Unix路径分隔符(也可以在Windows下运行),但您可能希望将其替换为您平台的目录分隔符。

如果你想保留文件名(不总是可取的,因为它在用户的控制下,国际字符可能会与文件系统编码冲突,等等),你可以这样做。我们现在只需要三个级别,所以3 * 3路径:

function storeFile($id, $source_path)
{
    GLOBAL $BASE_PATH;
    $seq = sprintf("%09d", $id); // Transforms 42 in 00000042

    $dir = str_split($seq, 3);  // Transforms in { 000 000 042 }

    // Now build directory. 042 is last directory

    $path = $BASE_PATH;
    foreach($dir as $component)
    {
        $path .= "/$component";
        if (!is_dir($path))
            mkdir($path);
    }

    // Use name component of source file
    $file = basename($source_path);

    // $path/$file is my file name, 042 in $path
    rename($source_path, $dest_path = "$path/$file");

    // Just to check. TRUE if everything was hunky dory; FALSE if something went bad.
    return file_exists($dest_path);
}

getPath保持不变,但现在需要09d而不是012d。如果您想限制为两个大小为3的级别,请进一步缩小为06d