使用ZipArchive()添加空字节进行存档的奇怪行为

我有一个简单的Zip创建脚本,它将一大堆文件复制到一个目录中,然后从该目录创建一个.zip文件。 这种方法听起来很简单,但它产生的档案有问题。

起初我很困惑,因为档案在7Zip,WinRar et cetera等等的事物中开放得很好。 但是,我们无法使用内置在存档打开程序中的Windows。 为了排除主服务器使用Nginx + PHPfpm + Fedora 16时遇到的任何问题,我还使用Apache和mod_php在Ubuntu服务器上运行,在更标准的服务器上进行了测试。

在这两种情况下,问题都是一样的:归档总是可以在纯zip中打开,但在Windows版本中失败。 经过一些随机挖掘后,我想出了在Notepad ++中打开文件的想法,以检查它的初始标题。

原来,Ziparchive()正在做两件它不应该做的事情。

第一个问题很简单:它将完整路径作为归档中的空路径。 它不应该,但它是。 这可能是由于我的递归,所以我可以忍受这一点 在这里输入图像描述

第二个问题是导致文件无法打开的大问题。 它在存档的开始部分预先添加一个空字节。 我所要做的就是在Notepad ++中手动打开文件,删除该字节然后保存,然后瞧:文件打开包括Windows在内的任何内容都没有问题。

**

我从来没有遇到过这种情况,Google很快就发现了许多Ziparchive()问题,但我找不到任何具体的东西。

这是我的Zip创建方法:

private function zipcreate($source, $destination) {
        if (!extension_loaded('zip') || !file_exists($source)) {
            return false;
        }
        $zip = new ZipArchive();
        if (!$zip->open($destination, ZIPARCHIVE::CREATE)) {
            return false;
        }
        $source = str_replace('', '/', realpath($source));
        if (is_dir($source) === true) {
            $files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($source), RecursiveIteratorIterator::SELF_FIRST);
            foreach ($files as $file) {
                $file = str_replace('', '/', realpath($file));
                if( in_array(substr($file, strrpos($file, '/')+1), array('.', '..')) )
                    continue;

                $file = realpath($file);

                if (is_dir($file) === true) {
                    $zip->addEmptyDir(str_replace($source . '/', '', $file . '/'));
                } else if (is_file($file) === true) {
                    $zip->addFromString(str_replace($source . '/', '', $file), file_get_contents($file));
                }
            }
        } else if (is_file($source) === true) {
            $zip->addFromString(basename($source), file_get_contents($source));
        }
        return $zip->close();
    }

通过执行调用:

$this->zipcreate($newdirpath, getcwd()."/$siteid-CompliancePack.zip");

主服务器上的参考phpinfo():

主服务器的phpinfo()

按照十六进制请求文件的前60个字节

[root@sid tmp]# od --format=x1 --read-bytes=60 54709-CompliancePack.zip
0000000 50 4b 03 04 14 00 00 00 08 00 39 4e 92 45 59 28
0000020 27 b3 37 53 00 00 00 f2 00 00 36 00 00 00 47 45
0000040 4e 45 52 41 4c 5f 4e 65 77 20 53 69 74 65 20 20
0000060 48 6f 77 61 72 74 68 20 54 69 6d 62
0000074
[root@sid tmp]#

新的发展:)所以我想我会尝试一些完全不同的东西! 我在Windows桌面上运行了一个WAMP堆栈(我通常只在Linux上进行测试和开发)。

我在Windows机器上运行门户网站,从Linux主服务器读取数据,与实时门户网站完全相同(只有不同​​的是在Linux上运行的实时门户!)

这次完全创建的文件差异是1个字节! 这与在同一台后端服务器上运行实时运行的代码完全相同,唯一的区别是用户服务器(门户)代码在Windows服务器上运行,而不是在Linux上运行。

Windows文件Linux文件

该文件由后端服务器以zip形式创建,然后由base64编码并通过Nusoap返回到门户服务器。 然后使用以下代码将文件直接流式传输到客户端浏览器。 SitesClass.downloadCompliancePack只是一个将所有文件移动到临时文件夹然后运行上面的zipcreate方法的方法,所以没有什么不可思议的。

$result = $client->call('SitesClass.downloadCompliancePack', array('appusername' => 'xxx','apppassword' => 'xxx','apikey' => 'xxx','siteid' => 54709));
    // Display the result
    header('Content-type: application/octet-stream');
    header('Content-disposition: attachment; filename="54709-CompliancePack.zip"');
    $base = json_decode($result[2]);
    echo base64_decode($base->FileData);

所以现在我更加困惑,因为一个简单的base64_decode在windows和linux之间不应该有所不同。

更新2015年1月

对不起,所有那些已经发布/帮助过的人都迟到了,我一直有点忙,只是想着看这个!

我已经根据下面发布的信息做了一些测试,我已经缩小了失败点的范围! 我现在确切知道它负责的一小部分代码。 请参阅下面的截图。 在这里输入图像描述

文本区域中的十六进制输出由以下代码创建。

<?php
// get configuration
include "system/config.php";
include "pages/pageclasses/carbon.class.php";
//////////// document action ///////////////
$sid = $_GET['sid'];
// Pull in the NuSOAP code
require_once('lib/nusoap.php');
// Create the client instance
$client = new nusoap_client($api_link); // using nosoap_client
// Call the SOAP method
$result = $client->call('SitesClass.downloadCompliancePack', array('appusername' => $api_username,'apppassword' => $api_password,'apikey' => $api_key,'siteid' => $sid));
// Display the result
$base = json_decode($result[2]);
echo "<textarea>".bin2hex(trim(base64_decode($base->FileData)))."</textarea>";
?>

另一个显示20(十六进制空格)的代码块在它之前

<?php
// get configuration
include "system/config.php";
include "pages/pageclasses/carbon.class.php";
//////////// document action ///////////////
$sid = $_GET['sid'];
// Pull in the NuSOAP code
require_once('lib/nusoap.php');
// Create the client instance
$client = new nusoap_client($api_link); // using nosoap_client
// Call the SOAP method
$result = $client->call('SitesClass.downloadCompliancePack', array('appusername' => $api_username,'apppassword' => $api_password,'apikey' => $api_key,'siteid' => $sid));
// Display the result
header('Content-type:application/octet-stream');
header('Content-disposition:attachment;filename="'.$sid.'-CompliancePack.zip"');
$base = json_decode($result[2]);
echo trim(base64_decode($base->FileData));
?>

在这两种情况下,代码运行在相同的前端Web服务器(linux)和相同的后端/ Web服务服务器(linux)上。唯一的区别是将文件数据输出到textarea,另一个将文件数据输出到浏览器在直接流中。

这两个代码块都是整个文件内容,并且在php打开之前或php关闭之后没有空格,并且只在头部()的安全一侧没有任何空格,在任何行的末尾都没有空格。

所以,现在我处于这种代码块的奇怪情况,似乎在其流式传输之前将一个随机空间添加到文件中

header('Content-type:application/octet-stream');
header('Content-disposition:attachment;filename="'.$sid.'-CompliancePack.zip"');
echo trim(base64_decode($base->FileData));

关于你的第一个问题,我会建议使用

  • ZipArchive :: addGlob http://php.net/manual/en/ziparchive.addglob.php或
  • ZipArchive :: addPattern()http://php.net/manual/en/ziparchive.addpattern.php
  • 这些函数具有额外的参数来操作文件名:

    “remove_path”

    在添加到存档之前从前缀中删除匹配的文件路径。

    他们似乎也在做文件系统遍历的工作。

    空字符可能与路径有关。

    他们在这里提到一个与旧文件有关的旧bug:http://grokbase.com/t/php/php-bugs/094pkepf54/48048-new-empty-files-corrupt-zip我并不认为它是相关的,但也许值得尝试删除空文件。 (用于测试)。


    关于文件开头的附加字节:

    该文件在服务器上生成时似乎没问题。 显然,问题来自传输/编码过程。

    检查实际提供文件的脚本。 例如,当你的服务器脚本如下所示:

    _<?php readfile('zipfile.zip');
    

    并且在脚本开始处有一个空格(用下划线表示)或任何其他字符,它将成为输出的一部分。

    如果角色不是脚本的一部分,请检查可能会破坏输出的包含脚本。

    根据新的代码示例更新:

    尝试在将二进制数据发送到浏览器之前清理输出缓冲区:

    header('Content-type:application/octet-stream');
    header('Content-disposition:attachment;filename="'.$sid.'-CompliancePack.zip"');
    ob_clean();
    echo trim(base64_decode($base->FileData));
    
    链接地址: http://www.djcxy.com/p/82935.html

    上一篇: Weird behaviour with ZipArchive() adding null bytes to archive

    下一篇: Autoscale Python Celery with Amazon EC2