PHP ZIP 文件下载

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10681844/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 22:48:05  来源:igfitidea点击:

PHP ZIP file download

phpzipdownload

提问by victor_golf

Here is a code which downloads attachment files from the an imap server. Almost all file types (pdf, doc, xls, etc) are being downloaded correctly, where as some zip files give the following error:

这是从 imap 服务器下载附件文件的代码。几乎所有文件类型(pdf、doc、xls 等)都被正确下载,其中一些 zip 文件会出现以下错误:

"The archive is either in unknown format or damaged"

“存档格式未知或已损坏”

Code:

代码:

//data from imap server
$name = "xyz 123.zip";
$type = "APPLICATION";
$subtype = "ZIP";
$encoding = "BASE64";
$body = imap_base64($data);

header('Content-Description: File Transfer');
header('Content-Type: '. $type .'/'. $subtype);
header('Content-Disposition: attachment; filename='.$name);
header('Content-Transfer-Encoding: '.$encoding);
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
ob_clean();
flush();
echo $body;

Also if I echo the data and convert it to a file using:

此外,如果我回显数据并将其转换为文件:

http://www.motobit.com/util/base64-decoder-encoder.asp

http://www.motobit.com/util/base64-decoder-encoder.asp

The file is being downloaded correctly. Hence no problem in getting the file from server. Where am I going wrong??

文件正在正确下载。因此从服务器获取文件没有问题。我哪里错了??

回答by iWizard

Script for downloading files in zip format: zip.php

下载zip格式文件的脚本: zip.php

<?php

/**
* Zip file creation class.
* Makes zip files.
*
* Last Modification and Extension By :
*
*  Hasin Hayder
*  HomePage : www.hasinme.info
*  Email : [email protected]
*  IDE : PHP Designer 2005
*
*
* Originally Based on :
*
*  http://www.zend.com/codex.php?id=535&single=1
*  By Eric Mueller <[email protected]>
*
*  http://www.zend.com/codex.php?id=470&single=1
*  by Denis125 <[email protected]>
*
*  a patch from Peter Listiak <[email protected]> for last modified
*  date and time of the compressed file
*
* Official ZIP file format: http://www.pkware.com/appnote.txt
*
* @access  public
*/
class zipfile
{
    /**
     * Array to store compressed data
     *
     * @var  array    $datasec
     */
    var $datasec      = array();

    /**
     * Central directory
     *
     * @var  array    $ctrl_dir
     */
    var $ctrl_dir     = array();

    /**
     * End of central directory record
     *
     * @var  string   $eof_ctrl_dir
     */
    var $eof_ctrl_dir = "\x50\x4b\x05\x06\x00\x00\x00\x00";

    /**
     * Last offset position
     *
     * @var  integer  $old_offset
     */
    var $old_offset   = 0;


    /**
     * Converts an Unix timestamp to a four byte DOS date and time format (date
     * in high two bytes, time in low two bytes allowing magnitude comparison).
     *
     * @param  integer  the current Unix timestamp
     *
     * @return integer  the current date in a four byte DOS format
     *
     * @access private
     */
    function unix2DosTime($unixtime = 0) {
        $timearray = ($unixtime == 0) ? getdate() : getdate($unixtime);

        if ($timearray['year'] < 1980) {
            $timearray['year']    = 1980;
            $timearray['mon']     = 1;
            $timearray['mday']    = 1;
            $timearray['hours']   = 0;
            $timearray['minutes'] = 0;
            $timearray['seconds'] = 0;
        } // end if

        return (($timearray['year'] - 1980) << 25) | ($timearray['mon'] << 21) | ($timearray['mday'] << 16) |
                ($timearray['hours'] << 11) | ($timearray['minutes'] << 5) | ($timearray['seconds'] >> 1);
    } // end of the 'unix2DosTime()' method

    /**
     *
     * Function to force the download of the archive as soon as it is created
     *
     * @param archiveName string - name of the created archive file
     * @access public
     * @return ZipFile via Header
     */
    public function forceDownload($archiveName) {
        if(ini_get('zlib.output_compression')) {
            ini_set('zlib.output_compression', 'Off');
        }

        // Security checks
        if( $archiveName == "" ) {
            echo "<html><title>Public Photo Directory - Download </title><body><BR><B>ERROR:</B> The download file was NOT SPECIFIED.</body></html>";
            exit;
        }
        elseif ( ! file_exists( $archiveName ) ) {
            echo "<html><title>Public Photo Directory - Download </title><body><BR><B>ERROR:</B> File not found.</body></html>";
            exit;
        }

        header("Pragma: public");
        header("Expires: 0");
        header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
        header("Cache-Control: private",false);
        header("Content-Type: application/zip");
        header("Content-Disposition: attachment; filename=".basename($archiveName).";" );
        header("Content-Transfer-Encoding: binary");
        header("Content-Length: ".filesize($archiveName));
        readfile("$archiveName");
    }

    /**
     * Adds "file" to archive
     *
     * @param  string   file contents
     * @param  string   name of the file in the archive (may contains the path)
     * @param  integer  the current timestamp
     *
     * @access public
     */
    function addFile($data, $name, $time = 0)
    {
        $name     = str_replace('\', '/', $name);

        $dtime    = dechex($this->unix2DosTime($time));
        $hexdtime = '\x' . $dtime[6] . $dtime[7]
                  . '\x' . $dtime[4] . $dtime[5]
                  . '\x' . $dtime[2] . $dtime[3]
                  . '\x' . $dtime[0] . $dtime[1];
        eval('$hexdtime = "' . $hexdtime . '";');

        $fr   = "\x50\x4b\x03\x04";
        $fr   .= "\x14\x00";            // ver needed to extract
        $fr   .= "\x00\x00";            // gen purpose bit flag
        $fr   .= "\x08\x00";            // compression method
        $fr   .= $hexdtime;             // last mod time and date

        // "local file header" segment
        $unc_len = strlen($data);
        $crc     = crc32($data);
        $zdata   = gzcompress($data);
        $zdata   = substr(substr($zdata, 0, strlen($zdata) - 4), 2); // fix crc bug
        $c_len   = strlen($zdata);
        $fr      .= pack('V', $crc);             // crc32
        $fr      .= pack('V', $c_len);           // compressed filesize
        $fr      .= pack('V', $unc_len);         // uncompressed filesize
        $fr      .= pack('v', strlen($name));    // length of filename
        $fr      .= pack('v', 0);                // extra field length
        $fr      .= $name;

        // "file data" segment
        $fr .= $zdata;

        // "data descriptor" segment (optional but necessary if archive is not
        // served as file)
        $fr .= pack('V', $crc);                 // crc32
        $fr .= pack('V', $c_len);               // compressed filesize
        $fr .= pack('V', $unc_len);             // uncompressed filesize

        // add this entry to array
        $this -> datasec[] = $fr;

        // now add to central directory record
        $cdrec = "\x50\x4b\x01\x02";
        $cdrec .= "\x00\x00";                // version made by
        $cdrec .= "\x14\x00";                // version needed to extract
        $cdrec .= "\x00\x00";                // gen purpose bit flag
        $cdrec .= "\x08\x00";                // compression method
        $cdrec .= $hexdtime;                 // last mod time & date
        $cdrec .= pack('V', $crc);           // crc32
        $cdrec .= pack('V', $c_len);         // compressed filesize
        $cdrec .= pack('V', $unc_len);       // uncompressed filesize
        $cdrec .= pack('v', strlen($name) ); // length of filename
        $cdrec .= pack('v', 0 );             // extra field length
        $cdrec .= pack('v', 0 );             // file comment length
        $cdrec .= pack('v', 0 );             // disk number start
        $cdrec .= pack('v', 0 );             // internal file attributes
        $cdrec .= pack('V', 32 );            // external file attributes - 'archive' bit set

        $cdrec .= pack('V', $this -> old_offset ); // relative offset of local header
        $this -> old_offset += strlen($fr);

        $cdrec .= $name;

        // optional extra field, file comment goes here
        // save to central directory
        $this -> ctrl_dir[] = $cdrec;
    } // end of the 'addFile()' method


    /**
     * Dumps out file
     *
     * @return  string  the zipped file
     *
     * @access public
     */
    function file()
    {
        $data    = implode('', $this -> datasec);
        $ctrldir = implode('', $this -> ctrl_dir);

        return
            $data .
            $ctrldir .
            $this -> eof_ctrl_dir .
            pack('v', sizeof($this -> ctrl_dir)) .  // total # of entries "on this disk"
            pack('v', sizeof($this -> ctrl_dir)) .  // total # of entries overall
            pack('V', strlen($ctrldir)) .           // size of central dir
            pack('V', strlen($data)) .              // offset to start of central dir
            "\x00\x00";                             // .zip file comment length
    } // end of the 'file()' method


    /**
     * A Wrapper of original addFile Function
     *
     * Created By Hasin Hayder at 29th Jan, 1:29 AM
     *
     * @param array An Array of files with relative/absolute path to be added in Zip File
     *
     * @access public
     */
    function addFiles($files /*Only Pass Array*/)
    {
        foreach($files as $file)
        {
        if (is_file($file)) //directory check
        {
            $data = implode("",file($file));
                    $new_file = $this->prefix_name.end(explode('/', $file));
                    $this->addFile($data, $new_file);
                }
        }
    }

    /**
     * A Wrapper of original file Function
     *
     * Created By Hasin Hayder at 29th Jan, 1:29 AM
     *
     * @param string Output file name
     *
     * @access public
     */
    function output($file)
    {
        $fp=fopen($file,"w");
        fwrite($fp,$this->file());
        fclose($fp);
    }



} // end of the 'zipfile' class
?>

Example usage:

用法示例:

<?php

include_once('zip.php');

$zip_file = 'my_files.zip'; // name for downloaded zip file

$ziper = new zipfile();
$ziper->prefix_name = 'folder/'; // here you create folder which will contain downloaded files
$ziper->addFiles($files_to_zip);  // array of files
$ziper->output($zip_file); 
$ziper->forceDownload($zip_file);
@unlink($zip_file);

?>

回答by Kamran Ali

I might be a little late but I faced the same problem and these lines worked like a charm for me

我可能迟到了,但我遇到了同样的问题,这些台词对我来说就像一个魅力

while (ob_get_level()) {
  ob_end_clean();
}

回答by Nana Partykar

It Worked For Me. It Download My .Zip File. But, you have to provide the full path of File, where it is located.
For ex: My File is inside assets/upload Folder. Attach name of that file with this location. You will download it without any problem.

$FilePaths='assets/Upload/'.$FileName; //Give proper file path with file name.

download_file($FilePaths);

function download_file( $fullPath )
{
  if( headers_sent() )
    die('Headers Sent');


  if(ini_get('zlib.output_compression'))
    ini_set('zlib.output_compression', 'Off');


  if( file_exists($fullPath) )
  {

    $fsize = filesize($fullPath);
    $path_parts = pathinfo($fullPath);
    $ext = strtolower($path_parts["extension"]);

    switch ($ext) 
    {
      case "pdf": $ctype="application/pdf"; break;
      case "exe": $ctype="application/octet-stream"; break;
      case "zip": $ctype="application/zip"; break;
      case "doc": $ctype="application/msword"; break;
      case "xls": $ctype="application/vnd.ms-excel"; break;
      case "ppt": $ctype="application/vnd.ms-powerpoint"; break;
      case "gif": $ctype="image/gif"; break;
      case "png": $ctype="image/png"; break;
      case "jpeg":
      case "jpg": $ctype="image/jpg"; break;
      default: $ctype="application/force-download";
    }

    header("Pragma: public"); 
    header("Expires: 0");
    header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
    header("Cache-Control: private",false); 
    header("Content-Type: $ctype");
    header("Content-Disposition: attachment; filename=\"".basename($fullPath)."\";" );
    header("Content-Transfer-Encoding: binary");
    header("Content-Length: ".$fsize);
    ob_clean();
    flush();
    readfile( $fullPath );

  } 
  else
    die('File Not Found');

}

For more info, Check this File Download

有关更多信息,请检查此文件下载

回答by Seth

I didn't see anyone mention this yet, but try removing the Content-Length header when download ZIP files. I had this exact same problem and it worked for

我还没有看到任何人提到这一点,但是在下载 ZIP 文件时尝试删除 Content-Length 标头。我有这个完全相同的问题,它适用于

回答by victor_golf

The culprit was imap_base64. It was not decoding the data correctly. I replaced it with base64_decode as follows and the code worked fine.

罪魁祸首是 imap_base64。它没有正确解码数据。我用 base64_decode 替换了它,如下所示,代码运行良好。

$body = base64_encode($data);

回答by Kalob Taulien

You're very close to the solution! I found the way to solve this was to remove the Content-type header all together and let the browser figure out that this was a .zip file. Remove the Content-Type and it should work as needed.

您已经非常接近解决方案了!我发现解决这个问题的方法是一起删除 Content-type 标头,让浏览器确定这是一个 .zip 文件。删除 Content-Type,它应该可以根据需要工作。

// Remove this line 
header('Content-Type: '. $type .'/'. $subtype);

In my console (Chrome) I was receiving a strange message(s) saying:

在我的控制台 (Chrome) 中,我收到一条奇怪的消息:

Resource interpreted as Document but transferred with MIME type application/octet-stream:

Resource interpreted as Document but transferred with MIME type application/zip:

资源被解释为 Document 但使用 MIME 类型 application/octet-stream 传输:

资源被解释为 Document 但使用 MIME 类型应用程序/zip 传输:

When I removed the MIME Type (Content-type header) the .zip stopped giving me an error and worked normally.

当我删除 MIME 类型(内容类型标头)时,.zip 停止给我错误并正常工作。

The most basic download script for making the .zip to work looked like this at the end: (note: You can still add more headers for caching and such)

使 .zip 工作的最基本的下载脚本在最后看起来像这样:(注意:您仍然可以添加更多用于缓存等的标头)

<?php
    header("Content-Disposition: attachment; filename=\"".$Filename."\"");
    header("Content-Length: ".filesize($Filename));
    readfile($Filename);
?>

Edited

已编辑

Your .zip's will also run into problems if the browser is reading extra spaces. You can prevent the extra spaces at the end of the readfile() by adding exit(); but you'll want to make sure you don't have any spaces at the start of the file. This is particularly common when dealing with frameworks (especially your own). A little trick: If you include all the functions, configs and classes before your buffer starts outputting HTML, view your source code. At the start there will be a space before the .

如果浏览器正在读取额外的空格,您的 .zip 也会遇到问题。您可以通过添加 exit(); 来防止 readfile() 末尾的多余空格;但您需要确保文件开头没有任何空格。这在处理框架(尤其是您自己的框架)时尤为常见。一个小技巧:如果在缓冲区开始输出 HTML 之前包含所有函数、配置和类,请查看源代码。开始时, . 之前会有一个空格。

回答by wormhit

This is really good for big file download. User will be receiving file immediately. Maybe zip will be correct too this way.

这对于大文件下载非常有用。用户将立即收到文件。也许 zip 这样也会正确。

    if(file_exists($filename) && is_readable($filename) && file_exists($filename)){
             header("Content-Disposition: attachment; filename=".basename(str_replace(' ', '_', $filename)));
             header("Content-Type: application/force-download");
             header("Content-Type: application/octet-stream");
             header("Content-Type: application/download");
             header("Content-Description: File Transfer");
             header("Content-Length: " . filesize($filename));
             flush(); // this doesn't really matter.

             $fp = fopen($filename, "r");
             while (!feof($fp))
             {
                 echo fread($fp, 65536);
                 flush(); // this is essential for large downloads
             }
             fclose($fp);
         exit;
    }