【问题标题】:Zlib is breaking CodeIgniter Zip Downloader and PHPExcelZlib 正在破坏 CodeIgniter Zip 下载器和 PHPExcel
【发布时间】:2019-06-10 16:24:18
【问题描述】:

我遇到的问题是,如果在 Web 服务器上启用了 Zlib 压缩,则在系统的 download_helper.php 文件中使用 CodeIgniter 的 force_download 函数下载任何 zip 文件会破坏存档文件并阻止用户打开它。

这是压缩文件后的force_download 函数,其中压缩了XLSX 文件:

    function force_download($filename = '', $data = '')
    {
        if ($filename == '' OR $data == '')
        {
            return FALSE;
        }

        // Try to determine if the filename includes a file extension.
        // We need it in order to set the MIME type
        if (FALSE === strpos($filename, '.'))
        {
            return FALSE;
        }

        // Grab the file extension
        $x = explode('.', $filename);
        $extension = end($x);

        // Load the mime types
        if (defined('ENVIRONMENT') AND is_file(APPPATH.'config/'.ENVIRONMENT.'/mimes.php'))
        {
            include(APPPATH.'config/'.ENVIRONMENT.'/mimes.php');
        }
        elseif (is_file(APPPATH.'config/mimes.php'))
        {
            include(APPPATH.'config/mimes.php');
        }

        // Set a default mime if we can't find it
        if ( ! isset($mimes[$extension]))
        {
            $mime = 'application/octet-stream';
        }
        else
        {
            $mime = (is_array($mimes[$extension])) ? $mimes[$extension][0] : $mimes[$extension];
        }

        // Generate the server headers
        if (strpos($_SERVER['HTTP_USER_AGENT'], "MSIE") !== FALSE)
        {
            header('Content-Type: "'.$mime.'"');
            header('Content-Disposition: attachment; filename="'.$filename.'"');
            header('Expires: 0');
            header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
            header("Content-Transfer-Encoding: binary");
            header('Pragma: public');
            header("Content-Length: ".strlen($data));
        }
        else
        {
            header('Content-Type: "'.$mime.'"');
            header('Content-Disposition: attachment; filename="'.$filename.'"');
            header("Content-Transfer-Encoding: binary");
            header('Expires: 0');
            header('Pragma: no-cache');
            header("Content-Length: ".strlen($data));
        }

        exit($data);
    }

启用 Zlib 还会在 Excel 文件中产生乱码,从而破坏 PHPExcel,如图所示。

单个XLSX文件的PHPExcel下载功能。这是整个 Excel 生成器、zip 和下载器函数的要点https://gist.github.com/TheWebAuthor/5773e56086df4317c7cf54aab45df328

ob_end_clean();                    
header('Content-Description: File Transfer');                            
header('Content-Type: application/vnd.ms-excel');                            
header('Content-Disposition: attachment; filename=' .$fileinfo['filename'] . '_' . $customerId . '_' . date("mdy") . '.xls');              
header('Content-Transfer-Encoding: binary');                            
header('Expires: 0');                            
header('Cache-Control: must-revalidate');                            
header('Pragma: public');                            
header('Content-Length: ' . filesize($file));                            
ob_clean();                            
flush();                            
readfile($file);

是否有适当的解决方法来启用 Zlib 而不会破坏这些功能?

【问题讨论】:

  • 如果我没记错的话,force_download 函数会为您发送标头。
  • 另外,您可能不应该使用pragma:public,因为它表示任何缓存都应该可以随意缓存文档。您可能正在查看缓存(和损坏)的文档,而不是文件的最新版本。
  • @S.Imp 将pragma:public 更改为pragma:no-cache,打开存档时仍然报错。
  • 请看下面的答案,如果我误解了,请澄清。
  • 那是什么版本的 Codeigniter? latest force_download 接受第三个参数。如果您的 Codeigniter 版本较旧,则它也可能缺少您正在下载的任何文件的正确 mime 类型。不幸的是,您发布的代码根本没有告诉我们您实际发送给用户的文件类型。

标签: php apache codeigniter phpexcel zlib


【解决方案1】:

您没有明确指出脚本的所有细节,但您开始描述“zip 文件”并提及“档案”。但是,您发布的小代码会发送一个 excel 文件的内容类型标头。如果您向浏览器发送一个 zip 文件,但告诉浏览器它的 mime 类型是 application/vnd.ms-excel,那么这可能是您的问题。

您是否考虑过只使用 force_download 函数?无论文件是 excel 还是 zip,此控制器都适用于我:

public function download() {
    $filepath = "/path/to/file.zip";
     // or you can send an Excel file
    $filepath = "/path/to/file.xls";

    $filename = basename($filepath);

    $data = file_get_contents($filepath);

    $this->load->helper('download');
    force_download($filename, $data);
}

force_download function has built-in code to sniff out the correct mime type based on the file extension。根据您的 Codeigniter 版本,您可能需要指定第三个参数 TRUE

我通过设置 application/vnd.ms-excel 的 MIME 类型但发送 ZIP 文件复制了您描述的“乱码”问题。相反,我仅使用 force_download 函数就成功下载了 ZIP 和 XLS 文件。

编辑 看过一些代码后,我想建议您围绕line 229 重新编写逻辑,因为在foreach 循环中检查count($result) 有点尴尬。您可以在进入 foreach 循环之前进行检查。

更重要的是,您未能在分配数组元素之前将$download_data 初始化为数组。根据您的环境和 error_reporting 设置,这可能会触发 E_NOTICE 或 E_WARNING,从而在您开始输出 zip 文件的内容之前导致额外的输出。如果您的代码在开始输出 zip 存档的实际二进制数据之前就输出了不需要的字符,这将损坏文件。另一种可能性是对未定义变量的引用将导致向标准输出发出 E_NOTICE 错误,这可能导致任何后续标头命令失败,因为一旦开始输出文本就无法发送标头命令。

另一种可能性是 MIME 类型不正确。您的 Codeigniter 版本很旧,但我认为这不太可能是一个问题。如果可以,请尝试检查发送到浏览器的响应标头,看看它们是 application/x-gzip 还是 application/zip 或其他。

最后,您应该检查appilcation/config.php 中的compress_output 设置。请注意那里的 cmets:

/*
|--------------------------------------------------------------------------
| Output Compression
|--------------------------------------------------------------------------
|
| Enables Gzip output compression for faster page loads.  When enabled,
| the output class will test whether your server supports Gzip.
| Even if it does, however, not all browsers support compression
| so enable only if you are reasonably sure your visitors can handle it.
|
| Only used if zlib.output_compression is turned off in your php.ini.
| Please do not use it together with httpd-level output compression.
|
| VERY IMPORTANT:  If you are getting a blank page when compression is enabled it
| means you are prematurely outputting something to your browser. It could
| even be a line of whitespace at the end of one of your scripts.  For
| compression to work, nothing can be sent before the output buffer is called
| by the output class.  Do not 'echo' any values with compression enabled.
|
*/

【讨论】:

  • @Daniel-harris 能解决这个问题吗?
  • 该功能已被使用,如问题中所述。
  • @DanielHarris 在您的问题中没有任何地方调用 force_download 函数。此外,您发布的“PHPExcel 下载功能的标题”代码发送了一个 excel 文件的 mime 类型。如果您正在输出 zip 文件但发送 XLS 文件的标头,您将收到您描述的乱码问题。如果您实际上正在使用 force_download 函数,请考虑提供第三个参数 TRUE 以强制 CodeIgniter 从文件扩展名中读取 mime 类型。
  • 这个问题暗示了由同一个问题(Zlib)引起的两个不同的问题(PHPExcel 和 CodeIgniter zip 下载器)。我在第一段中提到了force_download 函数。
猜你喜欢
  • 1970-01-01
  • 2017-12-04
  • 2023-03-24
  • 2023-02-22
  • 1970-01-01
  • 2015-09-29
  • 1970-01-01
  • 2018-10-20
  • 2017-11-20
相关资源
最近更新 更多