【问题标题】:Php: How to calculate Adler32 checksum for zip?Php:如何计算 zip 的 Adler32 校验和?
【发布时间】:2010-11-21 23:01:41
【问题描述】:

我在服务器端使用了 Paul Duncans php ZipStream (http://pablotron.org/software/zipstream-php/) 的组合,用于即时创建 zip,在 Flex/Air 客户端使用 Fzip (http://codeazur.com.br/lab/fzip/)。

在 Air 中运行良好,但在浏览器中运行 Flex 时,zip 需要在标头中包含 Adler32 校验和,以便读取 FZip。

如何计算 php 中 zip 的 Adler32 校验和?

ZipStream 核心函数,使用 gzdeflate 进行压缩,如下所示。

问候 / 乔纳斯

function add_file($name, $data, $opt = array(), $deflateLevel=0) {
    # compress data

    $zdata = gzdeflate($data, $deflateLevel);

    # calculate header attributes

    $crc  = crc32($data);
    $zlen = strlen($zdata);
    $len  = strlen($data);
    $meth = 0x08;

    # send file header
    $this->add_file_header($name, $opt, $meth, $crc, $zlen, $len);

    # print data
    $this->send($zdata);
}

private function add_file_header($name, $opt, $meth, $crc, $zlen, $len) {
    # strip leading slashes from file name
    # (fixes bug in windows archive viewer)
    $name = preg_replace('/^\\/+/', '', $name);

    # calculate name length
    $nlen = strlen($name);

    # create dos timestamp
    $opt['time'] = $opt['time'] ? $opt['time'] : time();
    $dts = $this->dostime($opt['time']);

    # build file header
    $fields = array(            # (from V.A of APPNOTE.TXT)
    array('V', 0x04034b50),     # local file header signature
    array('v', (6 << 8) + 3),   # version needed to extract
    array('v', 0x00),           # general purpose bit flag
    array('v', $meth),          # compresion method (deflate or store)
    array('V', $dts),           # dos timestamp
    array('V', $crc),           # crc32 of data
    array('V', $zlen),          # compressed data length
    array('V', $len),           # uncompressed data length
    array('v', $nlen),          # filename length
    array('v', 0),              # extra data len
    );

    # pack fields and calculate "total" length
    $ret = $this->pack_fields($fields);
    $cdr_len = strlen($ret) + $nlen + $zlen;

    # print header and filename
    $this->send($ret . $name);

    # add to central directory record and increment offset
    $this->add_to_cdr($name, $opt, $meth, $crc, $zlen, $len, $cdr_len);
}

【问题讨论】:

    标签: php apache-flex compression zip adler32


    【解决方案1】:

    Gumbo 的解决方案几乎是完美的——但如果参数是字符串而不是字节数组,则需要使用 ord() 来获取要处理的字符的 ascii 代码。像这样:

    function adler32($data) {
        $a = 1; $b = 0; $len = strlen($data);
        for ($index = 0; $index < $len; ++$index) {
            $a = ($a + ord($data[$index])) % 65521;
            $b = ($b + $a) % 65521;
        }
        return ($b << 16) | $a;
    }
    

    【讨论】:

      【解决方案2】:

      对于 PHP 5 >= 5.1.2,您可以使用 hash 函数,该函数返回 crc 的十六进制字符串表示:

      $dataStr = "abc";
      $crcStr = hash('adler32', $dataStr);
      

      更新:有一个bug in hash until mid 2009 的字节顺序是错误的。该错误似乎已在 5.2.11 和 5.3.0 及更高版本中修复。但对于早期版本,需要交换结果的字节顺序。

      更新 2:这是一个可用于解决该错误的包装函数(和一个测试):

      <?php
      
      error_reporting(E_ALL);
      
      function hash_adler32_wrapper($data) {
          $digHexStr = hash("adler32", $data);
      
          // If version is better than 5.2.11 no further action necessary
          if (version_compare(PHP_VERSION, '5.2.11', '>=')) {
              return $digHexStr;
          }
      
          // Workaround #48284 by swapping byte order
          $boFixed = array();
          $boFixed[0] = $digHexStr[6];
          $boFixed[1] = $digHexStr[7];
          $boFixed[2] = $digHexStr[4];
          $boFixed[3] = $digHexStr[5];
          $boFixed[4] = $digHexStr[2];
          $boFixed[5] = $digHexStr[3];
          $boFixed[6] = $digHexStr[0];
          $boFixed[7] = $digHexStr[1];
      
          return implode("", $boFixed);
      }
      
      // Test fixture, plus expected output generated using the adler32 from zlib
      $data_in = "abc";
      $expected_out = 0x024d0127;
      
      // PHP's hash function returns a hex hash value as a string so hexdec used to
      // convert to number
      $hash_out = hexdec(hash("adler32", $data_in));
      
      // Get value via the wrapper function
      $wrapper_out = hexdec(hash_adler32_wrapper($data_in));
      
      printf("data_in:          %s\n", $data_in);
      printf("expected_out:     0x%08x\n", $expected_out);
      printf("builtin hash out: 0x%08x, %s\n", $hash_out,
              ($hash_out == $expected_out)? "OK" : "NOT OK");
      printf("wrapper func out: 0x%08x, %s\n", $wrapper_out,
              ($wrapper_out == $expected_out)? "OK" : "NOT OK");
      
      ?>
      

      【讨论】:

        【解决方案3】:

        翻译自example implementation in the Wikipedia article:

        define('MOD_ADLER', 65521);
        
        function adler32($data) {
            $a = 1; $b = 0; $len = strlen($data);
            for ($index = 0; $index < $len; ++$index) {
                $a = ($a + $data[$index]) % MOD_ADLER;
                $b = ($b + $a) % MOD_ADLER;
            }
            return ($b << 16) | $a;
        }
        

        并将该整数值转换为字节:

        pack('H*', $checksum);
        

        【讨论】:

        • 谢谢,秋葵!我会测试它!乔纳斯
        猜你喜欢
        • 2012-03-30
        • 1970-01-01
        • 2017-03-09
        • 2020-07-02
        • 2011-02-04
        • 1970-01-01
        • 2014-03-02
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多