2013-04-03 92 views
12

我存储用户的文件,以自己名字命名的目录类似最好的方式来储存大量的用户数据的

/username/file01.jpg 
/username/file02.mp4 
/username/file03.mp3 

但是,如果更多的用户来上传多个文件,那么这造成问题,因为这将导致部分或许多用户迁移到另一个驱动器。我首先选择用户名目录解决方案,因为我不想要混合文件名。我不想更改文件名。另外如果另一个用户上传相同的文件名,那么它会产生问题,如果这些文件以原始名称存储。

什么能做到这一点的最好办法。我有一个解决方案,但想问社区是最好的方法。

我会用连续的文件夹,然后散列文件名的一些事情非常独特和存储到目录中。 我会做的是将文件和用户名的原始名称存储到数据库和存储在磁盘中的文件名的哈希值。

当有人要访问该文件,我会读该文件通过PHP要么更换名称或使得该文件被下载的原文件名会在这一点上做一些事情。

我只有这个建议的解决方案。你们有没有比这个更好的其他?

编辑:

我用的文件夹系统也是如此,并可能为第2方式,我将使用虚拟文件夹。 我的数据库是MongoDB

你们所有的答案都很棒,真的很有帮助。我想为每个人提供赏金,这就是为什么我离开它,社区可以自动提供。 谢谢你的答案,我真的很感激。

+3

我发现用户ID(不变的值)是组织上载的更好方法。手动导航比较困难(查看文件夹不会告诉你上传的是谁),但它可以让用户名更改而不会破坏与其对应的资产文件夹。 – 2013-04-03 21:59:39

+0

@JasonSperske嗨,但如果磁盘已满并且他们不断上传,我将如何处理用户及其文件。 – Abhishek 2013-04-03 22:04:55

+7

由于您要为每个文件创建一个数据库条目,因此您可以存储“存储卷ID”,每当存储卷上的空间用尽时就会增加。提取文件时,您将获得一个用户标识,文件哈希和存储卷名称,您可以组合该文件来检索该资产。我只是使用亚马逊S3,让他们处理这样的东西,然而 – 2013-04-03 22:07:28

回答

7

我处理数据库文件元数据,并用UUID retrive的文件。我做的是:基于

  1. 内容识别
    1. MD5从文件的内容
    2. 命名空间的UUID:V5基于用户的UUID和文件的MD5产生唯一的标识符。
    3. 基于'realname'生成路径的自定义函数。
    4. 保存在数据库上:UUID,ORIGINALNAME(上传的名称),真实姓名(生成的名称),文件大小和MIME。 (可选dateAdded和md5)
  2. 文件恢复。
    1. UUID以检索元数据。
    2. 根据实名重新生成文件路径。
    3. 原始名称用于向下载文件的用户显示熟悉的名称。

我处理文件的名称分配一个命名空间UUID作为数据库主密钥,生成基于用户和文件名的路径。前提是你的用户有一个uuid分配给他。以下代码将帮助您避免数据库上的ID冲突,并帮助您通过其内容识别文件(如果您需要有一种方法来查找重复内容而不是文件名)。

$fileInfo = pathinfo($_FILE['file']['name']); 
$extension = (isset($fileInfo['extension']))?".".$fileInfo['extension']:""; 

$md5Name = md5_file($_FILE['file']['tmp_name']); //you could use other hash algorithms if you are so inclined. 

$realName = UUID::v5($user->uuid, $md5Name) . $extension; //UUID::v5(namespace, value). 

我用一个函数基于一些自定义parameteres生成的文件路径,你可以使用$ username和$真实姓名。如果您实施可能已在文件命名方案或任何自定义方案上进行了分区的分布式文件夹结构,这会很有帮助。

function generateBasePath($realname, $customArgsArray){ 
    //Process Args as your requirements. 
    //might as well be "$FirstThreeCharsFromRealname/" 
    //or a checksum that helps you decide which drive/volume/mountpoint to use. 
    //like some files on the local disk and some other from an Amazon::S3 mountpoint. 
    return $mountpoint.'/'.$generatedPath; 
} 

作为额外的奖励,这也:

  1. 帮助您维护一个版本文件库,如果你添加的文件(UUID)的它已取代该文件的记录的属性。
  2. 如果添加“所有者”和/或“组”的属性,则创建应用程序访问控制列表。
  3. 也适用于单个文件夹结构。

注意:我使用php的$ _FILE作为基于此问题标签的文件源示例。它可以来自任何文件源或生成的内容。

9

你能创建关系MySQL表?例如: -

一个users表和files表。

您的用户表将跟踪所有的你(我认为)已经追踪:

idnameemail

然后把这些文件表格将存储是这样的:

id, fileExtension, fileSize,userID < ---- userID将是指向中的id字段的外键表。

那么当您保存文件,因为它是id你可以保存它。 fileExtension并使用查询来提取与该文件关联的用户或与用户关联的所有文件。

例如:

SELECT users.name, files.id, files.extension 
FROM `users` 
INNER JOIN `files` on users.id = files.userID; 
+1

,嗨,我不使用mysql,而是使用mongodb。但这种方式也更好。意味着你赞成数据库处理所有文件的细节。 – Abhishek 2013-04-04 07:42:01

2

由于文件系统是一棵树,而不是图(面式分类),它很难想出一些办法为它很简单地表示多个实体,如用户,媒体类型,日期,事件,图像作物类型等。这就是为什么使用关系数据库更容易 - 它可以转换为图形。

但是,由于它的另一个抽象层次,您需要自己编写可以进行低级别同步的函数,包括避免名称冲突,长路径名称,每个文件夹大文件数,每个实体传输的简便性,水平缩放等。所以这取决于你的应用程序需要多么复杂的是

0
  1. MongoDB中存储的实际文件名(如:myImage.jpg)和其他属性(例如:MIME类型),加上$random-text.jpg从2 & 3.下方

  2. 产生一些$random-text,如: base_convert(mt_rand(), 10, 36)uniqid($username, true);

  3. 物理存储文件为$random-text.jpg - 总是好的,保持相同的扩展

  4. 注:使用filter_var(),以确保输入文件名不会带来安全风险的MongoDB。

亚马逊S3是可靠和便宜的,请注意与S3的“最终并发”。

2

另一个策略是创建一个2维结构,其中的目录的第一电平是用户名的第2个字符,那么第二级是剩余的字符(类似于GIT中如何存储其SHA-1对象ID) 。例如:

/files/jr/andomuser/456.jpg 

用户'jrandomuser'。

请注意,如用户名可能不会被随机分布的SHA-1的值,则可能需要以后添加另一个级别。但是,怀疑它。

+0

你的想法确实令人印象深刻。我会考虑的。数据存储在磁盘中后仍存在的问题。如果在用户和保存文件夹的情况下如何解决这个问题。 由于亚马逊s3不允许folders.if他们做我会没有问题得到他们的存储,因为那么他们的问题,他们如何安排我的文件。 – Abhishek 2013-04-19 21:14:23

5

既然你已经使用MongoDB的,我建议检查出GridFS的。这是一个规范,允许您将文件(即使它们大于16mb)存储到MongoDB集合中。

它是可扩展的,所以你不会有任何问题,如果你添加另一台服务器,它还存储元数据,它可以读取数据块文件,该机还内置备份功能。

0

假设用户在数据库中有一个唯一的ID(主键),如果ID为73用户上传文件,保存这样的:

“上传/ $ userid_ $文件名$转”

例如,73_resume.doc,73_myphoto.jpg

现在,获取文件时,使用以下代码:

foreach (glob("uploads/$userid_*.*") as $filename) { 
    echo $filename; 
} 

这可以用散列的解决方案进行组合(存储在d B),因此获得下载路径为73_photo.jpg的用户不会在浏览器地址栏中随机尝试74_photo.jpg。

3

我会根据文件名,上传日期和时间以及文件名的用户名生成一个GUID,将这些值以及文件的路径保存到数据库中供以后使用。如果您生成这样的GUID,文件名不能被猜出。

举例来说,让用户Daniel Steiner(我)在2013年4月23日上午37点上传一个名为resume.doc的文件到您的服务器。这会给出基本值 Daniel_Steiner + 2013/23/04 + 00:37 +简历。doc然后将作为MD5哈希05c2d2f501e738b930885d991d136f1e。为确保文件将在正确的程序中打开,我们将在之后添加正确的文件结尾,并因此得到类似于http://link.to/your/site/05c2d2f501e738b930885d991d136f1e.doc的内容。如果您的useraccounts已经有用户标识,则可以将这些标识添加到URL中,例如,如果我的用户ID将是123145,url将是http://link.to/your/site/123145/05c2d2f501e738b930885d991d136f1e.doc

如果您将原始文件名保存到数据库中,您以后也可以提供下载脚本,该脚本提供该文件的原始文件名以供下载,甚至很难它具有另一个文件名你的服务器。

如果您可以使用符号链接,则将文件重定位到另一个硬盘上也不成问题。

如果你愿意,我可以想出一个PHP的例子 - 不应该是太多的代码。

2

我建议使用以下的数据库结构:

enter image description here

File表至少具有:

enter image description here

IDFileauto_increment柱/主键。 UserIDnullable外键。

对于FK_File_User我建议:

ON UPDATE NO ACTION -- IDUser is auto_increment too. No changes need to be tracked. 
ON DELETE SET NULL -- If user deleted, then File is not owned. Might be deleted 
        -- with CRON job or something else. 

不过,另一列可能被添加到File表:

  1. 实际上传的日期和时间
  2. 实际的mime-type
  3. 实际存储位置(用于分布式存储系统)
  4. D ownload计数(另一个表可能是一个更好的解决方案)

等等

一些好处:

  1. 你并不需要计算文件大小,散列扩展名或任何文件元,因为您可能通过一次数据库操作获得它。
  2. 您可以获得每个用户使用的文件数/空间的统计数据/无论您通过单个SELECT ... GROUP BY ... WITH ROLLUP语句写入File表,它将比分析可能跨多个存储设备分布的实际文件更快。
  3. 您可以为不同的用户应用文件访问权限。这将不会对表结构数据库造成重大改变。

我不认为作为一个选项,这需要在存储原始文件名,有两个原因:

  1. 文件可能有名字,这不是正确的服务器操作系统的文件系统的支持,像西里尔的。
  2. 两个不同的文件可能具有完全相同的名称,因此其中一个文件可能会被另一个文件覆盖。

所以,有一个解决方案:

1)重命名文件时,他们从INSERT上传到IDFileFile表。这是安全的,没有dublicates。

2)恢复的文件,当它需要/下载,喜欢的名字:

// peform query to "File" table by given ID 

list($name, $ext, $size, $md5) = $result->fetch_row(); 

$result->free(); 

header('Content-Length: ' . $size); 
header('Content-MD5: ' . $md5); 
header('Accept-Ranges: bytes'); 
header('Connection: close'); 
header('Content-Type: application/force-download'); 
header('Content-Disposition: attachment; filename="' . $name . '.' . $ext . '"'); 

// flush file content 

3)实际文件可以存储单个目录中(因为IDFile是安全的)和IDUser -named子目录 - 依赖在一个情况。

4)由于IDFile是一个直接序列,如果一些文件不见了,你可以通过评估实际文件名序列的缺失段来获得他们的数据库元。然后,您可以“告知所有者”,“删除文件元”或这两个操作。


我反对本身存储DBMS大实际文件的二进制内容的想法。

DBMS是关于数据和分析,它不是一个文件系统,并且不应该以那种方式使用,如果我的意见很重要。

+2

看起来非常像我的方法;)是的,我反对将二进制文件存储在数据库中! – 2013-04-23 09:52:11

1

您可以安装LDAP服务器。 LDAP查找速度非常快,因为它针对重读操作进行了高度优化。您甚至可以查询数据

LDAP以类似于时尚的树形式组织数据。

您可以按以下示例组织数据“用户 - > IP地址 - >文件夹 - >文件名”。通过这种方式,文件可以在物理上/地理上分散开来,并且可以非常快速地获取位置。

您也可以使用标准LDAP查询来查询,例如,获取特定用户的所有文件列表或获取文件夹中的文件列表等。

相关问题