Opensubtitles哈希函数无法处理大文件

10
我使用下面的函数来计算电影文件在opensubtitles.org上的哈希值。它大多数时候都有效,但对于大文件,我会收到以下错误提示。
我不太理解,因为数据应该总是可用的。
有人能指点我正确的方向吗?

PHP警告:unpack():类型v:输入不足,需要2个,实际为0,在文件.php的第169行


function OpenSubtitlesHash($file)
{
    $handle = fopen($file, "rb");
    $fsize = filesize($file);

    $hash = array(3 => 0, 
                  2 => 0, 
                  1 => ($fsize >> 16) & 0xFFFF, 
                  0 => $fsize & 0xFFFF);

    for ($i = 0; $i < 8192; $i++)
    {
        $tmp = ReadUINT64($handle);
        $hash = AddUINT64($hash, $tmp);
    }

    $offset = $fsize - 65536;
    fseek($handle, $offset > 0 ? $offset : 0, SEEK_SET);

    for ($i = 0; $i < 8192; $i++)
    {
        $tmp = ReadUINT64($handle);
        $hash = AddUINT64($hash, $tmp);         
    }

    fclose($handle);
        return UINT64FormatHex($hash);
}

function ReadUINT64($handle)
{
    $u = unpack("va/vb/vc/vd", fread($handle, 8));
    return array(0 => $u["a"], 1 => $u["b"], 2 => $u["c"], 3 => $u["d"]);
}

function AddUINT64($a, $b)
{
    $o = array(0 => 0, 1 => 0, 2 => 0, 3 => 0);

    $carry = 0;
    for ($i = 0; $i < 4; $i++) 
    {
        if (($a[$i] + $b[$i] + $carry) > 0xffff ) 
        {
            $o[$i] += ($a[$i] + $b[$i] + $carry) & 0xffff;
            $carry = 1;
        }
        else 
        {
            $o[$i] += ($a[$i] + $b[$i] + $carry);
            $carry = 0;
        }
    }

    return $o;   
}

function UINT64FormatHex($n)
{   
    return sprintf("%04x%04x%04x%04x", $n[3], $n[2], $n[1], $n[0]);
}

请指出 file.php 文件中的第 169 行。 - Justinas
在你的 $u = unpack("va/vb/vc/vd", fread($handle, 8)); 中,如果你执行 $var = fread($handle, 8);var_dump($var);,输出是什么?它实际上读取了数据吗? - Tschallacka
3个回答

5
如果您提供了一些额外的信息:系统版本、php版本、大文件的大小、文件类型(简单文件、url等),这将为准确的答案提供更多信息。
主要假设是您使用32位系统,并且在处理超过2GB的文件时遇到了filsize问题。从文档中可以看出:
注意:由于PHP的整数类型是有符号的,许多平台使用32位整数,因此一些文件系统函数可能会返回对于大于2GB的文件来说意料之外的结果。
您可能会得到错误的filesize值,因此无法准确地读取尾部字节。此评论解释了如何获取更大文件的大小,并指出fseek在内部使用int,因此您无法将指针放在2GB阈值之后。您需要使用fread来读取到该位置。
还有其他假设可以检查:
  • fread 可能在某些情况下读取的数据量超过了请求的数据量:

    如果流是带缓冲区的,并且它不代表一个纯文件,在最多一次读取至少等于块大小(通常为8192字节)的数据后,根据之前缓冲的数据,返回的数据大小可能会大于块大小。

  • stat 缓存会阻止您获得准确的文件大小值;

1
当$handle为null或false时,您从未检查过它是否有任何资源,因此会出现相同的错误。
PHP Warning: unpack(): Type v: not enough input, need 2, have 0 in file.php on line 169

在对$handle进行操作之前,请添加检查。

if(!is_null($handle)){
  // Do something..
}

0

您不需要也不应该计算总文件大小。如果文件大小超过PHP_INT_MAX,则结果将不准确。

一个更好的解决方案是从文件末尾使用fseek()

fseek($handle, -65536, SEEK_END);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接