我建议使用以下数据库结构:
表File
至少有:
IDFile
是auto_increment
列/主键。
UserID
是nullable
外键。
因为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
:
- 实际上传日期和时间
- 实际的 mime 类型
- 实际存储位置(用于分布式存储系统)
- 下载计数(另一个表可能是更好的解决方案)
ETC...
一些好处:
- 您不需要计算文件大小、哈希、扩展名或任何文件元,因为您可能通过一次数据库操作获得它。
File
您可以通过单个语句获取每个用户的文件计数/使用的空间/您写入表的任何内容的统计信息SELECT ... GROUP BY ... WITH ROLLUP
,这比分析可能分布在多个存储设备上的实际文件要快。
- 您可以为不同的用户应用文件访问权限。它不会对表结构数据库进行重大更改。
我不认为存储时需要原始文件名作为一种选择,原因有两个:
- 文件可能有名称,服务器操作系统文件系统不正确支持该名称,如西里尔文文件系统。
- 两个不同的文件可能具有完全相同的名称,因此其中一个可能会被另一个覆盖。
所以,有一个解决方案:
1) 将文件上传到表时重IDFile
命名文件INSERT
。File
这是安全的,没有重复。
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 是关于数据和分析的,它不是文件系统,如果我的拙见很重要,那么永远不应该以这种方式使用。