• PHPZip类 批量下载


    (转载自:http://www.yii-china.com/post/detail/471.html)

    前言:

        前一段时间,有人有同一个文件复制几份不同名字批量下载需求--有点奇葩(也许是我理解错了,只是批量下载罢了)

    参考来源:http://www.oschina.net/code/snippet_167936_6898 不过源码我做了部分修改。如果大家对这个类

                     有别的需求可以在下方留言,在我能力范围内,会尽量满足大家的需求。

    实现思路:

        方法一: 用php自带的zip扩展打包成  .zip文件下载,缺点要配置zip扩展,默认情况下php没有要去官网下载

        方法二: 读取一个个文件内容利用gzcompress()字符串压缩和pack()打包成zip(原理高大上,其实我也不太懂!)

    实现类如下:

            使用说明:这个与框架无关放到相应的地方修改一下命名空间即可

    <?php
    namespace OrgUtil;
    
    class PHPZip
    {
        private $ctrl_dir = array();
        private $datasec = array();
    
    
        /**********************************************************
         * 压缩部分
         **********************************************************/
        // ------------------------------------------------------ //
        // #遍历指定文件夹
        //
        // $archive  = new PHPZip();
        // $filelist = $archive->visitFile(文件夹路径);
        // print "当前文件夹的文件:<p>
    ";
        // foreach($filelist as $file)
        //     printf("%s<br>
    ", $file);
        // ------------------------------------------------------ //
        var $fileList = array();
    
        public function visitFile($path)
        {
            global $fileList;
            $path = str_replace("\", "/", $path);
            $fdir = dir($path);
    
            while (($file = $fdir->read()) !== false) {
                if ($file == '.' || $file == '..') {
                    continue;
                }
    
                $pathSub = preg_replace("*/{2,}*", "/", $path . "/" . $file);  // 替换多个反斜杠
                $fileList[] = is_dir($pathSub) ? $pathSub . "/" : $pathSub;
                if (is_dir($pathSub)) {
                    $this->visitFile($pathSub);
                }
            }
            $fdir->close();
            return $fileList;
        }
    
    
        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;
            }
    
            return (($timearray['year'] - 1980) << 25)
            | ($timearray['mon'] << 21)
            | ($timearray['mday'] << 16)
            | ($timearray['hours'] << 11)
            | ($timearray['minutes'] << 5)
            | ($timearray['seconds'] >> 1);
        }
    
    
        var $old_offset = 0;
    
        private function addFile($data, $filename, $time = 0)
        {
            $filename = str_replace('\', '/', $filename);
    
            $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 = "x50x4bx03x04";
            $fr .= "x14x00";
            $fr .= "x00x00";
            $fr .= "x08x00";
            $fr .= $hexdtime;
            $unc_len = strlen($data);
            $crc = crc32($data);
            $zdata = gzcompress($data);
            $c_len = strlen($zdata);
            $zdata = substr(substr($zdata, 0, strlen($zdata) - 4), 2);
            $fr .= pack('V', $crc);
            $fr .= pack('V', $c_len);
            $fr .= pack('V', $unc_len);
            $fr .= pack('v', strlen($filename));
            $fr .= pack('v', 0);
            $fr .= $filename;
    
            $fr .= $zdata;
    
            $fr .= pack('V', $crc);
            $fr .= pack('V', $c_len);
            $fr .= pack('V', $unc_len);
    
            $this->datasec[] = $fr;
            $new_offset = strlen(implode('', $this->datasec));
    
            $cdrec = "x50x4bx01x02";
            $cdrec .= "x00x00";
            $cdrec .= "x14x00";
            $cdrec .= "x00x00";
            $cdrec .= "x08x00";
            $cdrec .= $hexdtime;
            $cdrec .= pack('V', $crc);
            $cdrec .= pack('V', $c_len);
            $cdrec .= pack('V', $unc_len);
            $cdrec .= pack('v', strlen($filename));
            $cdrec .= pack('v', 0);
            $cdrec .= pack('v', 0);
            $cdrec .= pack('v', 0);
            $cdrec .= pack('v', 0);
            $cdrec .= pack('V', 32);
    
            $cdrec .= pack('V', $this->old_offset);
            $this->old_offset = $new_offset;
    
            $cdrec .= $filename;
            $this->ctrl_dir[] = $cdrec;
        }
    
    
        var $eof_ctrl_dir = "x50x4bx05x06x00x00x00x00";
    
        private function file()
        {
            $data = implode('', $this->datasec);
            $ctrldir = implode('', $this->ctrl_dir);
    
            return $data
            . $ctrldir
            . $this->eof_ctrl_dir
            . pack('v', sizeof($this->ctrl_dir))
            . pack('v', sizeof($this->ctrl_dir))
            . pack('V', strlen($ctrldir))
            . pack('V', strlen($data))
            . "x00x00";
        }
    
    
        // ------------------------------------------------------ //
        // #压缩到服务器
        //
        // $archive = new PHPZip();
        // $archive->Zip("需压缩的文件所在目录", "ZIP压缩文件名");
        // $archive->Zip("需压缩的文件路径列表", "ZIP压缩文件名");
        // ------------------------------------------------------ //
        public function Zip($dir, $saveName)
        {
            if (@!function_exists('gzcompress')) {
                return;
            }
    
            ob_end_clean();
            if(is_array($dir)){
                $filelist = $dir;
            }else{
                $filelist = $this->visitFile($dir);
            }
    
            if (count($filelist) == 0) {
                return;
            }
    
            foreach ($filelist as $file) {
                if (!file_exists($file) || !is_file($file)) {
                    continue;
                }
    
                $fd = fopen($file, "rb");
                $content = @fread($fd, filesize($file));
                fclose($fd);
    
                // 1.删除$dir的字符(./folder/file.txt删除./folder/)
                // 2.如果存在/就删除(/file.txt删除/)
                $file = substr($file, strlen($dir));
                if (substr($file, 0, 1) == "\" || substr($file, 0, 1) == "/") {
                    $file = substr($file, 1);
                }
    
                $this->addFile($content, $file);
            }
            $out = $this->file();
    
            $fp = fopen($saveName, "wb");
            fwrite($fp, $out, strlen($out));
            fclose($fp);
        }
    
    
        // ------------------------------------------------------ //
        // #压缩并直接下载
        //
        // $archive = new PHPZip();
        // $archive->ZipAndDownload("需压缩的文件所在目录",'保存文件名');没有后缀
        // $archive->ZipAndDownload("需压缩的文件路径列表",'保存文件名');
        // ------------------------------------------------------ //
        public function ZipAndDownload($dir,$savename='')
        {
            if (@!function_exists('gzcompress')) {
                return;
            }
    
            ob_end_clean();
            if(is_array($dir)){
                $filelist = $dir;
            }else{
                $filelist = $this->visitFile($dir);
            }
    
            if (count($filelist) == 0) {
                return;
            }
    
            foreach ($filelist as $file) {
                if (!file_exists($file) || !is_file($file)) {
                    continue;
                }
    
                $fd = fopen($file, "rb");
                $content = @fread($fd, filesize($file));
                fclose($fd);
    
                // 1.删除$dir的字符(./folder/file.txt删除./folder/)
                // 2.如果存在/就删除(/file.txt删除/)
                $file = substr($file, strlen($dir));
                if (substr($file, 0, 1) == "\" || substr($file, 0, 1) == "/") {
                    $file = substr($file, 1);
                }
    
                $this->addFile($content, $file);
            }
            $out = $this->file();
    
            if($savename==''){
                $savename = 'File'.date("YmdHis", time());
            }
    
            @header('Content-Encoding: none');
            @header('Content-Type: application/zip');
            @header('Content-Disposition: attachment ; filename=' . $savename . '.zip');
            @header('Pragma: no-cache');
            @header('Expires: 0');
            print($out);
        }
    
    
    
    
    
        /**********************************************************
         * 解压部分
         **********************************************************/
        // ------------------------------------------------------ //
        // ReadCentralDir($zip, $zipfile)
        // $zip是经过@fopen($zipfile, 'rb')打开的
        // $zipfile是zip文件的路径
        // ------------------------------------------------------ //
        private function ReadCentralDir($zip, $zipfile)
        {
            $size = filesize($zipfile);
            $max_size = ($size < 277) ? $size : 277;
    
            @fseek($zip, $size - $max_size);
            $pos = ftell($zip);
            $bytes = 0x00000000;
    
            while ($pos < $size) {
                $byte = @fread($zip, 1);
                $bytes = ($bytes << 8) | Ord($byte);
                $pos++;
                if ($bytes == 0x504b0506) {
                    break;
                }
            }
    
            $data = unpack('vdisk/vdisk_start/vdisk_entries/ventries/Vsize/Voffset/vcomment_size', fread($zip, 18));
    
            $centd['comment'] = ($data['comment_size'] != 0) ? fread($zip, $data['comment_size']) : '';  // 注释
            $centd['entries'] = $data['entries'];
            $centd['disk_entries'] = $data['disk_entries'];
            $centd['offset'] = $data['offset'];
            $centd['disk_start'] = $data['disk_start'];
            $centd['size'] = $data['size'];
            $centd['disk'] = $data['disk'];
            return $centd;
        }
    
    
        private function ReadCentralFileHeaders($zip)
        {
            $binary_data = fread($zip, 46);
            $header = unpack('vchkid/vid/vversion/vversion_extracted/vflag/vcompression/vmtime/vmdate/Vcrc/Vcompressed_size/Vsize/vfilename_len/vextra_len/vcomment_len/vdisk/vinternal/Vexternal/Voffset', $binary_data);
    
            $header['filename'] = ($header['filename_len'] != 0) ? fread($zip, $header['filename_len']) : '';
            $header['extra'] = ($header['extra_len'] != 0) ? fread($zip, $header['extra_len']) : '';
            $header['comment'] = ($header['comment_len'] != 0) ? fread($zip, $header['comment_len']) : '';
    
    
            if ($header['mdate'] && $header['mtime']) {
                $hour = ($header['mtime'] & 0xF800) >> 11;
                $minute = ($header['mtime'] & 0x07E0) >> 5;
                $seconde = ($header['mtime'] & 0x001F) * 2;
                $year = (($header['mdate'] & 0xFE00) >> 9) + 1980;
                $month = ($header['mdate'] & 0x01E0) >> 5;
                $day = $header['mdate'] & 0x001F;
                $header['mtime'] = mktime($hour, $minute, $seconde, $month, $day, $year);
            } else {
                $header['mtime'] = time();
            }
            $header['stored_filename'] = $header['filename'];
            $header['status'] = 'ok';
            if (substr($header['filename'], -1) == '/') {
                $header['external'] = 0x41FF0010;
            }  // 判断是否文件夹
            return $header;
        }
    
    
        private function ReadFileHeader($zip)
        {
            $binary_data = fread($zip, 30);
            $data = unpack('vchk/vid/vversion/vflag/vcompression/vmtime/vmdate/Vcrc/Vcompressed_size/Vsize/vfilename_len/vextra_len', $binary_data);
    
            $header['filename'] = fread($zip, $data['filename_len']);
            $header['extra'] = ($data['extra_len'] != 0) ? fread($zip, $data['extra_len']) : '';
            $header['compression'] = $data['compression'];
            $header['size'] = $data['size'];
            $header['compressed_size'] = $data['compressed_size'];
            $header['crc'] = $data['crc'];
            $header['flag'] = $data['flag'];
            $header['mdate'] = $data['mdate'];
            $header['mtime'] = $data['mtime'];
    
            if ($header['mdate'] && $header['mtime']) {
                $hour = ($header['mtime'] & 0xF800) >> 11;
                $minute = ($header['mtime'] & 0x07E0) >> 5;
                $seconde = ($header['mtime'] & 0x001F) * 2;
                $year = (($header['mdate'] & 0xFE00) >> 9) + 1980;
                $month = ($header['mdate'] & 0x01E0) >> 5;
                $day = $header['mdate'] & 0x001F;
                $header['mtime'] = mktime($hour, $minute, $seconde, $month, $day, $year);
            } else {
                $header['mtime'] = time();
            }
    
            $header['stored_filename'] = $header['filename'];
            $header['status'] = "ok";
            return $header;
        }
    
    
        private function ExtractFile($header, $to, $zip)
        {
            $header = $this->readfileheader($zip);
    
            if (substr($to, -1) != "/") {
                $to .= "/";
            }
            if (!@is_dir($to)) {
                @mkdir($to, 0777);
            }
    
            $pth = explode("/", dirname($header['filename']));
            for ($i = 0; isset($pth[$i]); $i++) {
                if (!$pth[$i]) {
                    continue;
                }
                $pthss .= $pth[$i] . "/";
                if (!is_dir($to . $pthss)) {
                    @mkdir($to . $pthss, 0777);
                }
            }
    
            if (!($header['external'] == 0x41FF0010) && !($header['external'] == 16)) {
                if ($header['compression'] == 0) {
                    $fp = @fopen($to . $header['filename'], 'wb');
                    if (!$fp) {
                        return (-1);
                    }
                    $size = $header['compressed_size'];
    
                    while ($size != 0) {
                        $read_size = ($size < 2048 ? $size : 2048);
                        $buffer = fread($zip, $read_size);
                        $binary_data = pack('a' . $read_size, $buffer);
                        @fwrite($fp, $binary_data, $read_size);
                        $size -= $read_size;
                    }
                    fclose($fp);
                    touch($to . $header['filename'], $header['mtime']);
    
                } else {
    
                    $fp = @fopen($to . $header['filename'] . '.gz', 'wb');
                    if (!$fp) {
                        return (-1);
                    }
                    $binary_data = pack('va1a1Va1a1', 0x8b1f, Chr($header['compression']), Chr(0x00), time(), Chr(0x00), Chr(3));
    
                    fwrite($fp, $binary_data, 10);
                    $size = $header['compressed_size'];
    
                    while ($size != 0) {
                        $read_size = ($size < 1024 ? $size : 1024);
                        $buffer = fread($zip, $read_size);
                        $binary_data = pack('a' . $read_size, $buffer);
                        @fwrite($fp, $binary_data, $read_size);
                        $size -= $read_size;
                    }
    
                    $binary_data = pack('VV', $header['crc'], $header['size']);
                    fwrite($fp, $binary_data, 8);
                    fclose($fp);
    
                    $gzp = @gzopen($to . $header['filename'] . '.gz', 'rb') or die("Cette archive est compress!");
    
                    if (!$gzp) {
                        return (-2);
                    }
                    $fp = @fopen($to . $header['filename'], 'wb');
                    if (!$fp) {
                        return (-1);
                    }
                    $size = $header['size'];
    
                    while ($size != 0) {
                        $read_size = ($size < 2048 ? $size : 2048);
                        $buffer = gzread($gzp, $read_size);
                        $binary_data = pack('a' . $read_size, $buffer);
                        @fwrite($fp, $binary_data, $read_size);
                        $size -= $read_size;
                    }
                    fclose($fp);
                    gzclose($gzp);
    
                    touch($to . $header['filename'], $header['mtime']);
                    @unlink($to . $header['filename'] . '.gz');
                }
            }
            return true;
        }
    
    
        // ------------------------------------------------------ //
        // #解压文件
        //
        // $archive   = new PHPZip();
        // $zipfile   = "ZIP压缩文件名";
        // $savepath  = "解压缩目录名";
        // $zipfile   = $unzipfile;
        // $savepath  = $unziptarget;
        // $array     = $archive->GetZipInnerFilesInfo($zipfile);
        // $filecount = 0;
        // $dircount  = 0;
        // $failfiles = array();
        // set_time_limit(0);  // 修改为不限制超时时间(默认为30秒)
        //
        // for($i=0; $i<count($array); $i++) {
        //     if($array[$i][folder] == 0){
        //         if($archive->unZip($zipfile, $savepath, $i) > 0){
        //             $filecount++;
        //         }else{
        //             $failfiles[] = $array[$i][filename];
        //         }
        //     }else{
        //         $dircount++;
        //     }
        // }
        // set_time_limit(30);
        //printf("文件夹:%d&nbsp;&nbsp;&nbsp;&nbsp;解压文件:%d&nbsp;&nbsp;&nbsp;&nbsp;失败:%d<br>
    ", $dircount, $filecount, count($failfiles));
        //if(count($failfiles) > 0){
        //    foreach($failfiles as $file){
        //        printf("&middot;%s<br>
    ", $file);
        //    }
        //}
        // ------------------------------------------------------ //
        public function unZip($zipfile, $to, $index = Array(-1))
        {
            $ok = 0;
            $zip = @fopen($zipfile, 'rb');
            if (!$zip) {
                return (-1);
            }
    
            $cdir = $this->ReadCentralDir($zip, $zipfile);
            $pos_entry = $cdir['offset'];
    
            if (!is_array($index)) {
                $index = array($index);
            }
            for ($i = 0; $index[$i]; $i++) {
                if (intval($index[$i]) != $index[$i] || $index[$i] > $cdir['entries']) {
                    return (-1);
                }
            }
    
            for ($i = 0; $i < $cdir['entries']; $i++) {
                @fseek($zip, $pos_entry);
                $header = $this->ReadCentralFileHeaders($zip);
                $header['index'] = $i;
                $pos_entry = ftell($zip);
                @rewind($zip);
                fseek($zip, $header['offset']);
                if (in_array("-1", $index) || in_array($i, $index)) {
                    $stat[$header['filename']] = $this->ExtractFile($header, $to, $zip);
                }
            }
    
            fclose($zip);
            return $stat;
        }
    
    
    
    
    
        /**********************************************************
         * 其它部分
         **********************************************************/
        // ------------------------------------------------------ //
        // #获取被压缩文件的信息
        //
        // $archive = new PHPZip();
        // $array = $archive->GetZipInnerFilesInfo(ZIP压缩文件名);
        // for($i=0; $i<count($array); $i++) {
        //     printf("<b>&middot;%s</b><br>
    ", $array[$i][filename]);
        //     foreach($array[$i] as $key => $value)
        //         printf("%s => %s<br>
    ", $key, $value);
        //     print "
    <p>------------------------------------<p>
    
    ";
        // }
        // ------------------------------------------------------ //
        public function GetZipInnerFilesInfo($zipfile)
        {
            $zip = @fopen($zipfile, 'rb');
            if (!$zip) {
                return (0);
            }
            $centd = $this->ReadCentralDir($zip, $zipfile);
    
            @rewind($zip);
            @fseek($zip, $centd['offset']);
            $ret = array();
    
            for ($i = 0; $i < $centd['entries']; $i++) {
                $header = $this->ReadCentralFileHeaders($zip);
                $header['index'] = $i;
                $info = array(
                    'filename' => $header['filename'],                   // 文件名
                    'stored_filename' => $header['stored_filename'],            // 压缩后文件名
                    'size' => $header['size'],                       // 大小
                    'compressed_size' => $header['compressed_size'],            // 压缩后大小
                    'crc' => strtoupper(dechex($header['crc'])),    // CRC32
                    'mtime' => date("Y-m-d H:i:s", $header['mtime']),  // 文件修改时间
                    'comment' => $header['comment'],                    // 注释
                    'folder' => ($header['external'] == 0x41FF0010 || $header['external'] == 16) ? 1 : 0,  // 是否为文件夹
                    'index' => $header['index'],                      // 文件索引
                    'status' => $header['status']                      // 状态
                );
                $ret[] = $info;
                unset($header);
            }
            fclose($zip);
            return $ret;
        }
    
    
        // ------------------------------------------------------ //
        // #获取压缩文件的注释
        //
        // $archive = new PHPZip();
        // echo $archive->GetZipComment(ZIP压缩文件名);
        // ------------------------------------------------------ //
        public function GetZipComment($zipfile)
        {
            $zip = @fopen($zipfile, 'rb');
            if (!$zip) {
                return (0);
            }
            $centd = $this->ReadCentralDir($zip, $zipfile);
            fclose($zip);
            return $centd[comment];
        }
    }
    
    ?>

    使用方法:更多用法看源码实现--代码通俗易懂^v^

     $archive  = new PHPZip();
     $archive->Zip($zipdir, $savename);//文件夹压缩
     $archive->Zip($filelist, $savename);//文件列表压缩
     $archive->ZipAndDownload($zipdowndir);//压缩下载
     $archive->ZipAndDownload($filelist);//文件列表压缩下载

    另外付下载原理--构造如下http头部:

    $expire  =180;  //下载内容浏览器缓存时间
    $showname       //下载显示的文件名
    $length         //文件长度
    $type           //文件后缀
          header("Pragma: public");
          header("Cache-control: max-age=".$expire);
          //header('Cache-Control: no-store, no-cache, must-revalidate');
          header("Expires: " . gmdate("D, d M Y H:i:s",time()+$expire) . "GMT");
          header("Last-Modified: " . gmdate("D, d M Y H:i:s",time()) . "GMT");
          header("Content-Disposition: attachment; filename=".$showname);
          header("Content-Length: ".$length);
          header("Content-type: ".$type);
          header('Content-Encoding: none');
          header("Content-Transfer-Encoding: binary" );
          readfile($filename);
  • 相关阅读:
    Atitit 华为基本法 attilax读后感
    Atitit 华为管理者内训书系 以奋斗者为本 华为公司人力资源管理纲要 attilax读后感
    Atitit 项目版本管理gitflow 与 Forking的对比与使用
    Atitit 管理的模式扁平化管理 金字塔 直线型管理 垂直管理 水平管理 矩阵式管理 网状式样管理 多头管理 双头管理
    Atitit 乌合之众读后感attilax总结 与读后感结构规范总结
    深入理解 JavaScript 异步系列(4)—— Generator
    深入理解 JavaScript 异步系列(3)—— ES6 中的 Promise
    深入理解 JavaScript 异步系列(2)—— jquery的解决方案
    深入理解 JavaScript 异步系列(1)——基础
    使用 github + jekyll 搭建个人博客
  • 原文地址:https://www.cnblogs.com/xiangcaiduoyidian/p/6519068.html
Copyright © 2020-2023  润新知