【问题标题】:How to calculate sha256 for large files in PHP如何在 PHP 中计算大文件的 sha256
【发布时间】:2017-08-19 17:56:24
【问题描述】:

我想请教您如何在 PHP 中计算大文件的 sha256。目前,我使用 Amazon Glacier 存储旧文件并使用其 API 上传存档。最初,我只使用无法达到 MB 大小图像的小文件。当我尝试上传超过 1MB 的数据时,API 响应说我给他们的校验和与他们计算的不同。

这是我上传文件的代码:

//get the sha256 using the file path
$image = //image path;
$sha256 = hash_file("sha256", $image);

$archive = $glacier->uploadArchive([
            'accountId' => '', 
            'body' => "",
            'checksum' => $sha256,
            'contentSHA256' => $sha256,
            'sourceFile' => $image,
            'vaultName' => 'my-vault'
        ]);

还有错误:

AWS HTTP error: Client error: `POST https://glacier.us-west-2.amazonaws.com/vaults/70/archives` resulted in a `400 Bad Request` response:{"code":"InvalidParameterValueException","message":"Checksum mismatch: expected 9f1d4da29b6ec24abde48cb65cc32652ff589467 (truncated...)

我尝试了下面的函数来检查最终的哈希值,但是当我打印它时它似乎不是正确的哈希值:

private function getFinalHash($file)
{
    $fp = fopen($file, "r");
    $ctx = hash_init('sha256');
    while (!feof($fp)) {
        $buffer = fgets($fp, 1024);
        hash_update($ctx, $buffer);
    }
    $hash = hash_final($ctx, true); print_r($hash);exit;
    fclose($fp);

}

生成的哈希是这样的:ŸM¢›nÂJ½äŒ¶\Ã&RÿX”gíÖ'„IoA\C÷×

Amazon Glacier API 文档显示了如何计算校验和,如下所述:

对于每个 1 MB 的有效负载数据块,计算 SHA-256 哈希值。最后一块数据可以小于 1 MB。例如,如果您要上传一个 3.2 MB 的存档,则为前三个 1 MB 数据块中的每一个计算 SHA-256 哈希值,然后计算剩余 0.2 MB 数据的 SHA-256 哈希值。这些哈希值构成了树的叶节点。

我认为提供校验和的正确方法与提供校验和有关,但我不知道应该如何使用 PHP 处理大文件。我真的需要你的帮助。

【问题讨论】:

  • fgets 作为第二个参数采用 length 以字节而不是千字节为单位。这意味着您不会通过 1 mb,而是 1 kb。您应该将它乘以 1024 以获得 1 MB 块,但它不会对您有太大帮助,因为 fgets 读取文件直到它到达 lengthnewline。因此,如果您在大小为 1.2 MB 的文件上使用 fgets(),那么如果该文件有很多行,您可能会得到多于 2 个块。
  • 您的代码有一个非常根本的问题。 checksum 是一个具有 1MiB 块大小的 树形哈希,而 contentSHA256 是一个线性哈希,但您为两者都传递了 $sha256。这两个值不可能相同除非文件小于 1 MiB。
  • @Michael-sqlbot,是的,它们对于小于 1MB 的大小是相同的。他们提供了用于获取 C# 和 Java 校验和的代码,但没有用于 PHP 的示例,这就是为什么我尝试使用 getFinalHash 函数来获取确切的哈希值。
  • @Michael-sqlbot,我尝试手动添加 sha256(我对文件使用了在线哈希计算器)并留下 contentsha256 参数,因为如果不填写它将提供,现在的问题是我提供的 sha256 与亚马逊计算的 sha256 不匹配。

标签: php amazon-web-services zend-framework2 sha256 amazon-glacier


【解决方案1】:

Glacier 有自己的方法来计算 SHA256-TREE-HASH。 这里有关于 PHP 的工作代码。 此函数根据需要返回从 1MB 部分创建的 SHA256 哈希。它非常适合我,即使是大文件或小文件。

private function getFinalHash($path, $MB = 1048576)
{
    $fp = fopen($path, "rb");
    $hashes = [];
    while (($buffer = fread($fp, $MB))!=="") {
        $hashes[] = hash("sha256", $buffer, true);
    }
    if(count($hashes)==1){
        return bin2hex($hashes[0]);
    }
    while(true){
        $hashes_new = [];
        foreach($hashes as $k => $hash){
            if ($k % 2 == 0) {
                if(isset($hashes[$k+1])){
                    $hashes_new[] = hash("sha256", $hash.$hashes[$k+1], true);
                }
            }
        }
        if(count($hashes)>2 && count($hashes) % 2 != 0){
            $hashes_new[] = $hashes[count($hashes)-1];
        }
        if(count($hashes_new)>1){
            $hashes = $hashes_new;
        }else{
            fclose($fp);
            return bin2hex($hashes_new[0]);
        }
    }
}

【讨论】:

    【解决方案2】:

    诀窍在于,sha256 哈希是由您正在使用的适用于 PHP 的 AWS 开发工具包计算的。 所以你不需要自己计算哈希。 这是一个例子:

    $client = new GlacierClient(array(
        'key'    => '[aws access key]',
        'secret' => '[aws secret key]',
        'region' => '[aws region]', // (e.g., us-west-2) )); $result = 
    $client->uploadArchive(array(
            'vaultName' => $vaultName,
            'body'      => fopen($filename, 'r'), )); 
    $archiveId = $result->get('archiveId');
    

    【讨论】:

      猜你喜欢
      • 2023-04-10
      • 1970-01-01
      • 2020-09-01
      • 2011-12-12
      • 2012-11-06
      • 1970-01-01
      • 1970-01-01
      • 2020-02-09
      • 2018-01-27
      相关资源
      最近更新 更多