比较文件和目录的最佳方法是什么?假设我想将文件存储在服务器或服务器集合上,比如基于云的系统。我的用户在许多情况下彼此协作,而在有些情况下则不是。不管怎样,我都可以让一百多人拥有相同的文件。唯一的关键区别是它们重命名了文件。但基本上都是相同的数据。另一件事是没有特定的文件类型。有pdf, doc, docx, txt,视频,音频文件等。但这个问题可以归结为一遍又一遍的相同文件。我想做的就是把它砍下来。删除数百个副本,并在数据库的帮助下存储用户提供的文件名等内容,这样我就可以依次存储留下的单个文件,同时仍然提供他们使用的基本信息。
现在我知道我可以用md5或sha1或sha2或类似的东西做一些事情,这些东西基本上会给我一个唯一的值,我可以用来进行这种比较。但我不确定如何或从哪里开始。例如,如何使用php获得文件的sha或md5 ?当我查找的东西,我得到字符串的方法,而不是文件..
总的来说,我在这里寻找反弹的想法周围弄清楚这不是那么多的直接手段…任何帮助都太好了。
$filePath = '/var/www/site/public/uploads/foo.txt'
$data = file_get_contents($filePath);
$key = sha1($data); //or $key = sha1_file($filePath);
将$key保存在表的列中,并将该列标记为UNIQUE,这样默认情况下不会存储相同的文件。
使用sha1而不是md5,因为许多版本控制系统如git使用sha1哈希本身来识别文件的唯一性
当文件上传时:
- 计算哈希(SHA1等)
- 将文件重命名为该哈希并存储它(除非具有该哈希的文件已经存在[您已经拥有它])
- 在数据库中存储哈希值
当一个文件被请求时:
- 从数据库中获取哈希值
- 返回基于哈希的文件
- 使用HTTP头,这样用户的浏览器就会提供给他们使用的文件名
获取$path
文件的md5哈希值
$hash = md5(file_get_contents($path));
希望这部分回答了你的问题。
有许多方法可以完成这样一个系统。但是如果我必须从头开始写,我很可能会这样做:
-
有三个数据库表(伪代码):
table users { id integer ## PK username string password string ## sha1 ... } table user_files { user_id integer ## Composite INDEX file_id integer ## filename string } table files { id integer ## PK uniq_id string ## basically 'yyyMMddhhmmssRRRR' INDEX sha_hash string ## sha1 md5_hash string ## md5 }
其中
files.sha_hash
为计算该文件的sha1
的结果,files.md5_hash
为计算同一文件的md5
的结果,作为双重安全检查,files.filename
为实际文件名。在服务器上,将存储该文件并将其重命名为files.uniq_id
,以确保没有名称冲突,其中最后一个RRRR
字符表示一个随机字符串(循环RRRR
,直到uniq_id
在数据库中是唯一的)注意: PHP提供
sha1_file
和md5_file
。在计算文件时使用这些! -
当用户存储文件时,处理该文件(步骤1中描述)并适当保存。为了避免在服务器上的同一文件夹中有太多文件,您可以分解
files.uniq_id
并将每个文件分离到yyyy/MM
子文件夹中。下一步,关联
user_files.file_id = files.id
和user_files.user_id = users.id
,并设置user_files.filename
为上传的文件名(参见下一步)。 -
如果用户上传了其他文件,则按2处理,但要检查是否匹配到其他
files.sha_hash
、files.md5_hash
。在这一点上,如果我们有一个匹配,不管文件名是什么,它很可能是完全相同的文件,所以将找到的user_files.file_id = files.id
和user_files.user_id = users.id
关联起来,并将user_files.filename
设置为上传的文件名。注意:这将导致服务器上有
1
物理文件和2
虚拟文件。 -
如果用户不修改文件重命名,只需重命名
user_files.filename
匹配他/她想重命名的文件 -
如果用户删除一个文件,检查有多少个
user_files.file_id
匹配,只有找到1
匹配,才删除物理文件和files
条目。否则,只需删除user_files
关联。 -
如果用户修改或不重命名文件,执行删除(步骤5)和另一个保存(步骤3)
您可以使用:
md5(file_get_contents($filename));
为文件生成一个散列。
考虑到这一点,两个完全不同的文件将产生完全相同的md5哈希(其他哈希也是同样的问题,尽管您可以使用比md5更好的哈希方法来减少冲突)。要比较两个文件,您需要逐个字节地进行比较,但您不希望分析硬盘上每个文件的每个字节以找到匹配项。您需要做的是将数据库中每个文件的哈希值存储在a列中,该列也应该是索引。
然后您可以从数据库中选择与新文件具有相同哈希值的所有文件。这会给你一个小的文件列表。假设光盘上有10万个文件。您可能会得到与散列匹配的几个文件的列表。大多数时候,名单都很短。然后你可以一个字节一个字节地遍历这些文件,看看是否匹配。搜索具有相同哈希值的约10个文件的列表将使您不必搜索所有100,000个文件,但是您仍然需要逐个字节地进行比较,因为这10个文件可能都非常不同。
- 有必要吗?现在硬盘很便宜,谁还在乎那些副本呢?我想应该没有那么大吧?
- MD5等不是唯一的。这只是说两个文件不相同的一种快速方式。两个文件可能有相同的MD5值,但包含不同的数据。