唯一标识服务器上的文件和目录,以便进行比较


Uniquely identify Files and Directories on a Server for Comparison

比较文件和目录的最佳方法是什么?假设我想将文件存储在服务器或服务器集合上,比如基于云的系统。我的用户在许多情况下彼此协作,而在有些情况下则不是。不管怎样,我都可以让一百多人拥有相同的文件。唯一的关键区别是它们重命名了文件。但基本上都是相同的数据。另一件事是没有特定的文件类型。有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));

希望这部分回答了你的问题。

有许多方法可以完成这样一个系统。但是如果我必须从头开始写,我很可能会这样做:

  1. 有三个数据库表(伪代码):

    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_filemd5_file。在计算文件时使用这些!

  2. 当用户存储文件时,处理该文件(步骤1中描述)并适当保存。为了避免在服务器上的同一文件夹中有太多文件,您可以分解files.uniq_id并将每个文件分离到yyyy/MM子文件夹中。

    下一步,关联user_files.file_id = files.iduser_files.user_id = users.id,并设置user_files.filename为上传的文件名(参见下一步)。

  3. 如果用户上传了其他文件,则按2处理,但要检查是否匹配到其他files.sha_hashfiles.md5_hash。在这一点上,如果我们有一个匹配,不管文件名是什么,它很可能是完全相同的文件,所以将找到的user_files.file_id = files.iduser_files.user_id = users.id关联起来,并将user_files.filename设置为上传的文件名。

    注意:这将导致服务器上有1物理文件和2虚拟文件。

  4. 如果用户不修改文件重命名,只需重命名user_files.filename匹配他/她想重命名的文件

  5. 如果用户删除一个文件,检查有多少个user_files.file_id匹配,只有找到1匹配,才删除物理文件和files条目。否则,只需删除user_files关联。

  6. 如果用户修改或不重命名文件,执行删除(步骤5)和另一个保存(步骤3)

您可以使用:

md5(file_get_contents($filename));

为文件生成一个散列。

考虑到这一点,两个完全不同的文件将产生完全相同的md5哈希(其他哈希也是同样的问题,尽管您可以使用比md5更好的哈希方法来减少冲突)。要比较两个文件,您需要逐个字节地进行比较,但您不希望分析硬盘上每个文件的每个字节以找到匹配项。

您需要做的是将数据库中每个文件的哈希值存储在a列中,该列也应该是索引。

然后您可以从数据库中选择与新文件具有相同哈希值的所有文件。这会给你一个小的文件列表。假设光盘上有10万个文件。您可能会得到与散列匹配的几个文件的列表。大多数时候,名单都很短。然后你可以一个字节一个字节地遍历这些文件,看看是否匹配。搜索具有相同哈希值的约10个文件的列表将使您不必搜索所有100,000个文件,但是您仍然需要逐个字节地进行比较,因为这10个文件可能都非常不同。

  1. 有必要吗?现在硬盘很便宜,谁还在乎那些副本呢?我想应该没有那么大吧?
  2. MD5等不是唯一的。这只是说两个文件不相同的一种快速方式。两个文件可能有相同的MD5值,但包含不同的数据。