TL;DR:我有一个CMS系统,它使用SHA-1文件内容作为文件名来存储附件(不透明文件)。既然我已经知道SHA-1散列匹配两个文件,那么如何验证上传的文件是否真的与存储中的一个匹配呢?我想要高性能的。
长版本:
当用户将一个新文件上传到系统时,我计算上传的文件内容的SHA-1散列,然后检查存储后端是否已经存在具有相同哈希的文件。PHP在代码运行之前将上传的文件放入/tmp中,然后对上传的文件运行sha1sum以获取文件内容的SHA-1散列。然后,我从计算出来的SHA-1散列计算扇出,并决定NFS挂载目录层次结构下的存储目录。(例如,如果文件内容的SHA-1哈希为37aefc1e145992f2cc16fabadcfe23eede5fb094,则永久文件名为/nfs/data/files/37/ae/fc1e145992f2cc16fabadcfe23eede5fb094。)除了保存实际的文件内容外,我还为用户提交了元数据(例如,INSERT、原始文件名、日期标记等),将一个新行保存到SQL数据库中。
我目前正在解决的问题是,一个新上传的文件具有与存储后端中现有哈希匹配的SHA-1哈希。我知道,这种偶然发生的变化非常低,但我想确定一下。(关于目的情况,请参见https://shattered.io/)
给定两个文件名$file_a 和 $file_b**,,如何快速检查两个文件的内容是否相同?**假设文件太大,无法加载到内存中。对于Python,我会使用filecmp.cmp(),但是PHP似乎没有类似的东西。我知道,如果找到不匹配的字节,可以使用fread()和中止操作,但我不想编写这段代码。
发布于 2013-09-17 12:58:03
如果您已经有了一个SHA1和,则只需执行以下操作:
if ($known_sha1 == sha1_file($new_file))否则
if (filesize($file_a) == filesize($file_b)
&& md5_file($file_a) == md5_file($file_b)
)检查文件大小,以在一定程度上防止哈希冲突(这已经是非常不可能的)。还可以使用MD5,因为它比SHA算法要快得多(但不那么独特)。如果您希望更少的机会发生碰撞,请使用sha1_file()。
这就是如何准确地比较两个文件之间的相互关系。
这将比本机哈希函数运行得慢得多。
function compareFiles($file_a, $file_b)
{
if (filesize($file_a) != filesize($file_b))
return false;
$chunksize = 4096;
$fp_a = fopen($file_a, 'rb');
$fp_b = fopen($file_b, 'rb');
try
{
while (!feof($fp_a) && !feof($fp_b))
{
$d_a = fread($fp_a, $chunksize);
$d_b = fread($fp_b, $chunksize);
if ($d_a === false || $d_b === false || $d_a !== $d_b)
return false;
}
return true;
}
finally
{
fclose($fp_a);
fclose($fp_b);
}
}发布于 2013-09-17 12:33:40
更新
如果您想确保文件是相等的,那么您应该首先检查文件大小,如果它们匹配,那么只需要区分文件内容。这比使用哈希函数要快得多,并且肯定会给出正确的结果。
如果使用md5_file()或sha1_file()或其他hash_function散列内容,则不需要将整个文件内容加载到内存中。下面是一个使用md5的示例
$hash = md5_file('big.file'); // big.file is 1GB in my test
var_dump(memory_get_peak_usage());输出:
int(330540)在你的例子中,应该是:
if(md5_file('FILEA') === md5_file('FILEB')) {
echo 'files are equal';
}进一步注意,当您使用哈希函数时,您总是需要在复杂性和碰撞概率(这意味着两个不同的消息产生相同的哈希)之间作出决定。
发布于 2013-09-17 13:19:30
当您的文件很大并且是二进制文件时,您可以从几个偏移量中测试其中的几个字节。它应该比任何散列函数都快得多,特别是函数返回的结果是第一个不同的字符。
但是,这种方法不能适用于只有几个不同字符的文件。这是最好的大型档案,视频等。
function areFilesEqual($filename1, $filename2, $accuracy)
{
$filesize1 = filesize($filename1);
$filesize2 = filesize($filename2);
if ($filesize1===$filesize2) {
$file1 = fopen($filename1, 'r');
$file2 = fopen($filename2, 'r');
for ($i=0; $i<$filesize1 && $i<$filesize2; $i+=$accuracy) {
fseek($file1, $i);
fseek($file2, $i);
if (fgetc($file1)!==fgetc($file2)) return false;
}
fclose($file1);
fclose($file2);
return true;
}
return false;
}https://stackoverflow.com/questions/18849927
复制相似问题