首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >使用纯PHP验证两个文件是否相同?

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

Stack Overflow用户
提问于 2013-09-17 12:31:17
回答 7查看 24.9K关注 0票数 21

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()和中止操作,但我不想编写这段代码。

EN

回答 7

Stack Overflow用户

回答已采纳

发布于 2013-09-17 12:58:03

如果您已经有了一个SHA1和,则只需执行以下操作:

代码语言:javascript
运行
复制
if ($known_sha1 == sha1_file($new_file))

否则

代码语言:javascript
运行
复制
if (filesize($file_a) == filesize($file_b)
    && md5_file($file_a) == md5_file($file_b)
)

检查文件大小,以在一定程度上防止哈希冲突(这已经是非常不可能的)。还可以使用MD5,因为它比SHA算法要快得多(但不那么独特)。如果您希望更少的机会发生碰撞,请使用sha1_file()

这就是如何准确地比较两个文件之间的相互关系。

这将比本机哈希函数运行得慢得多。

代码语言:javascript
运行
复制
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);
    }
}
票数 29
EN

Stack Overflow用户

发布于 2013-09-17 12:33:40

更新

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

如果使用md5_file()sha1_file()或其他hash_function散列内容,则不需要将整个文件内容加载到内存中。下面是一个使用md5的示例

代码语言:javascript
运行
复制
$hash = md5_file('big.file'); // big.file is 1GB  in my test
var_dump(memory_get_peak_usage());

输出:

代码语言:javascript
运行
复制
int(330540)

在你的例子中,应该是:

代码语言:javascript
运行
复制
if(md5_file('FILEA') === md5_file('FILEB')) {
    echo 'files are equal';
}

进一步注意,当您使用哈希函数时,您总是需要在复杂性和碰撞概率(这意味着两个不同的消息产生相同的哈希)之间作出决定。

票数 8
EN

Stack Overflow用户

发布于 2013-09-17 13:19:30

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

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

代码语言:javascript
运行
复制
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;
}
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/18849927

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档