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

Mik*_*nen 15 php performance file-io file-upload

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.)除了保存实际文件内容外,我还在INSERTSQL数据库中为用户提交的元数据添加了新行(例如Content-Type,原始文件名) ,datestamp等).

我目前正在弄清楚的一个案例是新上传的文件具有与存储后端中的现有哈希匹配的SHA-1哈希.我知道事故发生的变化是天文数字低,但我想确定.(有关用例,请参阅https://shattered.io/)

由于两个文件名$file_a以及$file_b,如何快速检查,如果这两个文件具有相同的内容是什么?假设文件太大而无法加载到内存中.使用Python,我会使用,filecmp.cmp()但PHP似乎没有任何类似的东西.我知道fread()如果找到一个不匹配的字节就可以完成和中止,但我宁愿不写那个代码.

Cob*_*ast 17

如果你已经有一个SHA1总和,你可以简单地做:

if ($known_sha1 == sha1_file($new_file))
Run Code Online (Sandbox Code Playgroud)

除此以外

if (filesize($file_a) == filesize($file_b)
    && md5_file($file_a) == md5_file($file_b)
)
Run Code Online (Sandbox Code Playgroud)

检查文件大小,以防止哈希冲突(这已经非常不可能).同样使用MD5,因为它比SHA算法快得多(但不那么独特).


更新:

这是如何将两个文件完全相互比较的方法.

function compareFiles($file_a, $file_b)
{
    if (filesize($file_a) == filesize($file_b))
    {
        $fp_a = fopen($file_a, 'rb');
        $fp_b = fopen($file_b, 'rb');

        while (($b = fread($fp_a, 4096)) !== false)
        {
            $b_b = fread($fp_b, 4096);
            if ($b !== $b_b)
            {
                fclose($fp_a);
                fclose($fp_b);
                return false;
            }
        }

        fclose($fp_a);
        fclose($fp_b);

        return true;
    }

    return false;
}
Run Code Online (Sandbox Code Playgroud)

  • `fread($fp_a, 4096)` 在 EOF 处返回空字符串 `""`。所以这个循环是无限的。你应该添加`while (!feof($fp_a) && ($b = fread($fp_a, 4096)) !== false)` (2认同)

hek*_*mgl 6

更新

如果您想确保文件相等,那么您应该首先检查文件大小,如果它们匹配,则只需比较文件内容。这比使用散列函数快得多,并且肯定会给出正确的结果。


如果您使用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());
Run Code Online (Sandbox Code Playgroud)

输出:

int(330540)
Run Code Online (Sandbox Code Playgroud)

在您的示例中,它将是:

if(md5_file('FILEA') === md5_file('FILEB')) {
    echo 'files are equal';
}
Run Code Online (Sandbox Code Playgroud)

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