Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/php/281.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
使用纯PHP验证两个文件是否相同?_Php_Performance_File Upload_File Io - Fatal编程技术网

使用纯PHP验证两个文件是否相同?

使用纯PHP验证两个文件是否相同?,php,performance,file-upload,file-io,Php,Performance,File Upload,File Io,TL;DR:我有一个CMS系统,它使用文件内容的SHA-1作为文件名存储附件(不透明文件)。考虑到我已经知道SHA-1哈希匹配两个文件,如何验证上传的文件是否真的匹配存储中的一个文件?我想要高性能的 长版本: 当用户将新文件上载到系统时,我计算上载文件内容的SHA-1散列,然后检查存储后端中是否已经存在具有相同散列的文件。PHP在我的代码运行之前将上传的文件放入/tmp,然后我对上传的文件运行sha1sum,以获得文件内容的SHA-1哈希。然后,我从计算出的SHA-1散列计算扇出,并确定NFS装

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
)除了保存实际文件内容外,我还为用户提交的元数据在SQL数据库中插入一行(例如,
内容类型
、原始文件名、日期戳等)

我目前正在考虑的一个极端情况是,新上传的文件具有与存储后端中现有哈希匹配的SHA-1哈希。我知道,意外发生的更改非常少,但我想确定一下。(有关故意的情况,请参阅)


给定两个文件名
$file\u a
$file\u b
,如何快速检查两个文件是否具有相同的内容?
假设文件太大,无法加载到内存中。对于Python,我会使用
filecmp.cmp()
,但PHP似乎没有类似的功能。我知道这可以通过
fread()实现
并在发现不匹配字节时中止,但我不想编写该代码。

更新

如果要确保文件相等,则应首先检查文件大小,如果它们匹配,则只需区分文件内容。这比使用哈希函数快得多,并且肯定会给出正确的结果


如果使用
md5_file()
sha1_file()
或其他哈希函数对内容进行哈希运算,则无需将整个文件内容加载到内存中。下面是使用
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';
}

进一步注意,当您使用散列函数时,您总是需要在复杂性和冲突概率之间做出决定(这意味着两条不同的消息产生相同的散列)另一方面。

像您一样使用Sha1哈希。如果它们相等,也比较它们的md5哈希和文件大小。
如果您随后遇到一个在所有3次检查中都匹配但不相等的文件-您刚刚找到了圣杯:D

如果您已经有一个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算法快得多(但不那么独特)


更新:

这就是如何精确地比较两个文件

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');
        
    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)
        {
            fclose($fp_a);
            fclose($fp_b);
            return false;
        }
    }
 
    fclose($fp_a);
    fclose($fp_b);
          
    return true;
}

当你的文件是大的二进制文件时,你可以从几个偏移量中测试几个字节。它应该比任何散列函数都要快,特别是该函数通过第一个不同的字符返回结果。

但是,这种方法不适用于只有几个不同字符的文件。它最适合于大型档案、视频等。

函数arefileequal($filename1,$filename2,$accurity)
{
$filesize1=文件大小($filename1);
$filesize2=文件大小($filename2);
如果($filesize1==$filesize2){
$file1=fopen($filename1,'r');
$file2=fopen($filename2,'r');

对于($i=0;$i),下面的代码帮助您检查文件是否相同

/***check equality of files*/

$file1="pics/star.jpg";

$file2="pics/dupe.jpg";

if(sha1_file($file1)==sha1_file($file2))

echo "Identical";

else

echo "Not Identical";

所以我遇到了这个问题,然后找到了一个问题,这个问题回答了这个问题,而且确实有效

2021年…事情变了,所以我想我会发布一个链接到这个答案

A) 基本上,它使用了如上所示的
fopen
fread
,但它是有效的。对于我来说,被接受的答案总是不同的,即使是在同一个文件上

B) 如果您可以使用,那么
fopen
fread
方法将比sha1或md5方法更快,我不明白您为什么不能

Svish的版本来自上面的链接

function files_are_equal($a, $b)
{
  // Check if filesize is different
  if(filesize($a) !== filesize($b))
      return false;

  // Check if content is different
  $ah = fopen($a, 'rb');
  $bh = fopen($b, 'rb');

  $result = true;
  while(!feof($ah))
  {
    if(fread($ah, 8192) != fread($bh, 8192))
    {
      $result = false;
      break;
    }
  }

  fclose($ah);
  fclose($bh);

  return $result;
}

你是否试图避免散列冲突?使用散列是一个好主意。正如你所提到的,冲突的概率非常低-所以你可以确定在一般情况下,它是可以的。如果没有-让我们知道你对这些文件内容的情况:pgit正在使用sha1,所以我认为你使用sha1是足够安全的:)我正在尝试避免发生冲突bly因为散列冲突而丢失了文件内容。是的,如果我看到冲突,我会保留这两个文件。我敢打赌,在这种情况下,我会发现我的永久存储已被比特化。(在任何存储设备上获得随机位错误的变化似乎比查找SHA-1冲突要大得多;在这种情况下,我仍然希望获得损坏文件的新副本。)@Kakawait:
git
在相信文件是相同的之前,也会进行字节比较测试,因为SHA-1哈希恰好匹配,据我所知。@hek2mgl:谢谢,我不知道PHP实现是否足够健全,无法将整个文件读入内存。我不需要使用
shell_exec()
sha1sum
处理大文件。是的,它们经常被忘记:)…还可以看看其他可能更快的散列函数。但是这些函数必须使用
shell\u exec()
againI不会声称
f
function files_are_equal($a, $b)
{
  // Check if filesize is different
  if(filesize($a) !== filesize($b))
      return false;

  // Check if content is different
  $ah = fopen($a, 'rb');
  $bh = fopen($b, 'rb');

  $result = true;
  while(!feof($ah))
  {
    if(fread($ah, 8192) != fread($bh, 8192))
    {
      $result = false;
      break;
    }
  }

  fclose($ah);
  fclose($bh);

  return $result;
}