简体   繁体   中英

Zlib is breaking CodeIgniter Zip Downloader and PHPExcel

I'm facing an issue where if Zlib compression is enabled on the web server, downloading any zip file using CodeIgniter's force_download function in the system's download_helper.php file breaks the archive file and prevents users from opening it.

在此处输入图片说明

Here is the force_download function for zipped files after compressing XLSX files in it:

    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);
    }

Enabling Zlib also breaks PHPExcel by producing garbled characters in Excel files as you can see in this image.

在此处输入图片说明

PHPExcel download function for a single XLSX file. Here's the gist for the entire Excel generator, zip, and downloader functions 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);

Is there a proper workaround to enable Zlib and not breaking these features?

You haven't clearly indicated all the details of your script, but you start off describing a "zip file" and referring to "archives." However, what little code you have posted sends a content-type header for an excel file. If you send a zip file to the browser but tell the browser its mime type is application/vnd.ms-excel then this could be your problem.

Have you considered just using the force_download function? This controller works for me whether the file is excel or 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);
}

The force_download function has built-in code to sniff out the correct mime type based on the file extension . Depending on your version of Codeigniter, you may need to specify a third parameter of TRUE .

I have replicated the "garbled character" problem you describe by setting a mime type of application/vnd.ms-excel but sending a ZIP file. Conversely, I have successfully downloaded both ZIP and XLS files by just using the force_download function.

EDIT Having seen some code, I'd like to suggest that you rework your logic around line 229 as it is a little bit awkward to be checking count($result) inside your foreach loop. You might check that before entering a foreach loop.

More importantly, you fail to initialize $download_data as an array before assigning array elements. Depending on what your environment and error_reporting settings are, this may trigger an E_NOTICE or E_WARNING that causes additional output before you start to output the contents of your zip file. If your code ends up outputting unwanted characters before it starts outputting the actual binary data of your zip archive, this will corrupt the file. Another possibility is that a reference to the undefined variable will cause an E_NOTICE error to be emitted to stdout, which can cause any subsequent header commands to fail because you cannot send header commands once you have begun outputting text.

Another possibility is an incorrect mime type. Your version of Codeigniter is quite old, but I think it's unlikely that this is a problem. If you can, try inspecting the response headers sent to your browser to see if they are application/x-gzip or application/zip or something else.

Finally, you should check the compress_output setting in appilcation/config.php . Please note the comments there:

/*
|--------------------------------------------------------------------------
| 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.
|
*/

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM