12

我将用户文件存储在他们自己的名称目录中,例如

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

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

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

我将使用顺序文件夹,然后将文件名散列到一些非常独特的东西并存储到目录中。我要做的是将文件的原始名称和用户名存储到数据库中,并将文件名的哈希值存储在磁盘中。

当任何人想要访问该文件时,我将通过 php 读取该文件,或者替换名称,或者在此时执行某些操作,以便将文件作为原始文件名下载。

我脑子里只有这个建议的解决方案。你们有没有比这个更好的。

编辑:

我也使用文件夹系统,可能第二种方式我将使用虚拟文件夹。我的数据库是 MongoDB

伙计们,你所有的答案都很棒而且很有帮助。我想给每个人赏金,这就是我离开它的原因,以便社区可以自动提供。谢谢大家的回答。我真的很感激。

4

10 回答 10

9

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

users一桌files一桌。

您的用户表将跟踪您(我假设)已经跟踪的所有内容:

id, name,email

然后文件表将存储如下内容:

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

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

例如:

SELECT users.name, files.id, files.extension
FROM `users`
INNER JOIN `files` on users.id = files.userID;
于 2013-04-03T22:59:36.343 回答
7

我处理数据库上的文件元数据并使用 UUID 检索文件。我要做的是:

  1. 基于内容的识别
    1. 来自文件内容的 MD5
    2. 命名空间 UUID:v5 根据用户的 uuid 和文件的 md5 生成唯一标识符。
    3. 基于“实名”生成路径的自定义函数。
    4. 保存在数据库中:uuid、originalname(上传的名称)、realname(生成的名称)、filesize 和 mime。(可选的添加日期和 md5)
  2. 文件检索。
    1. 用于检索元数据的 UUID。
    2. 根据实名重新生成文件路径。
    3. Originalname 用于向下载文件的用户显示熟悉的名称。

我处理文件名,为其分配一个命名空间 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).

我使用一个函数根据一些自定义参数生成文件路径,您可以使用 $username 和 $realname。如果您实现可能已按文件命名方案或任何自定义方案分区的分布式文件夹结构,这将很有帮助。

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 作为基于此问题标签的文件源的示例。它可以来自任何文件源或生成的内容。

于 2013-04-20T20:49:14.877 回答
5

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

它是可扩展的,所以如果你添加另一个服务器就没有问题,它还存储元数据,可以分块读取文件,它还具有内置的备份功能。

于 2013-04-21T14:23:37.930 回答
3

我会根据文件名的哈希值、上传的日期和时间以及文件名的用户名生成一个 GUID,保存这些值,以及数据库中文件的路径以供以后使用。如果您生成这样的 GUID,则无法猜测文件名。

例如,让用户 Daniel Steiner(我)在 2013 年 4 月 23 日凌晨 12 点 37 分将名为 resume.doc 的文件上传到您的服务器。这将给出 Daniel_Steiner+2013/23/04+00:37+resume.doc 的基值,然后将其作为 MD5 哈希 05c2d2f501e738b930885d991d136f1e。为确保文件将在正确的程序中打开,我们随后将添加正确的文件结尾,因此将获得类似http://link.to/your/site/05c2d2f501e738b930885d991d136f1e.doc如果您的用户帐户已经有用户 ID ,您可以将这些添加到 URL,例如,如果我的用户 ID 为 123145,则 url 将为http://link.to/your/site/123145/05c2d2f501e738b930885d991d136f1e.doc

如果您将原始文件名保存到数据库中,您以后还可以提供一个下载脚本,为该文件提供其原始文件名以供下载,即使它在您的服务器上还有另一个文件名。

如果您可以使用符号链接,那么将文件重新定位到另一个硬盘上也应该不是问题。

如果您愿意,我也可以提出一个 PHP 示例 - 代码不应该太多。

于 2013-04-22T22:40:32.483 回答
2

由于文件系统是一棵树,而不是图(分面分类),因此很难想出某种方法来轻松表示多个实体,如用户、媒体类型、日期、事件、图像裁剪类型等。这就是使用关系数据库的原因更容易 - 它可以转换为图形。

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

于 2013-04-16T13:35:42.833 回答
2

另一种策略是创建一个二维结构,其中第一级目录是用户名的前 2 个字符,然后第二级是其余字符(类似于 Git 存储其 SHA-1 对象 ID 的方式)。例如:

/files/jr/andomuser/456.jpg

对于用户“jrandomuser”。

请注意,由于用户名可能不会像 SHA-1 值那样随机分布,您可能需要稍后添加另一个级别。怀疑它,虽然。

于 2013-04-19T18:51:17.373 回答
2

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

在此处输入图像描述

File至少有:

在此处输入图像描述

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 类型
  3. 实际存储位置(用于分布式存储系统)
  4. 下载计数(另一个表可能是更好的解决方案)

ETC...

一些好处:

  1. 您不需要计算文件大小、哈希、扩展名或任何文件元,因为您可能通过一次数据库操作获得它。
  2. File您可以通过单个语句获取每个用户的文件计数/使用的空间/您写入表的任何内容的统计信息SELECT ... GROUP BY ... WITH ROLLUP,这比分析可能分布在多个存储设备上的实际文件要快。
  3. 您可以为不同的用户应用文件访问权限。它不会对表结构数据库进行重大更改。

我不认为存储时需要原始文件名作为一种选择,原因有两个:

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

所以,有一个解决方案:

1) 将文件上传到表时重IDFile命名文件INSERTFile这是安全的,没有重复。

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 是关于数据和分析的,它不是文件系统,如果我的拙见很重要,那么永远不应该以这种方式使用。

于 2013-04-23T08:18:52.967 回答
1

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

LDAP 以树状方式组织数据。

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

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

于 2013-04-23T11:21:48.827 回答
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 造成安全风险。

Amazon S3 可靠且便宜,请注意 S3 的“最终并发”。

于 2013-04-18T23:48:13.120 回答
0

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

“上传/$userid_$filename.$ext”

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

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

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

这可以与散列解决方案(存储在数据库中)结合使用,以便获得下载路径为 73_photo.jpg 的用户不会在浏览器地址栏中随机尝试 74_photo.jpg。

于 2013-04-22T06:25:49.437 回答