You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

501 lines
19 KiB

  1. <?php
  2. namespace App\Common\Utils;
  3. class Zip
  4. {
  5. private $ctrl_dir = array();
  6. private $datasec = array();
  7. /**********************************************************
  8. * 压缩部分
  9. **********************************************************/
  10. // ------------------------------------------------------ //
  11. // #遍历指定文件夹
  12. //
  13. // $archive = new PHPZip();
  14. // $filelist = $archive->visitFile(文件夹路径);
  15. // print "当前文件夹的文件:<p>\r\n";
  16. // foreach($filelist as $file)
  17. // printf("%s<br>\r\n", $file);
  18. // ------------------------------------------------------ //
  19. var $fileList = array();
  20. public function visitFile($path)
  21. {
  22. global $fileList;
  23. $path = str_replace("\\", "/", $path);
  24. $fdir = dir($path);
  25. while (($file = $fdir->read()) !== false) {
  26. if ($file == '.' || $file == '..') {
  27. continue;
  28. }
  29. $pathSub = preg_replace("*/{2,}*", "/", $path . "/" . $file); // 替换多个反斜杠
  30. $fileList[] = is_dir($pathSub) ? $pathSub . "/" : $pathSub;
  31. if (is_dir($pathSub)) {
  32. $this->visitFile($pathSub);
  33. }
  34. }
  35. $fdir->close();
  36. return $fileList;
  37. }
  38. private function unix2DosTime($unixtime = 0)
  39. {
  40. $timearray = ($unixtime == 0) ? getdate() : getdate($unixtime);
  41. if ($timearray['year'] < 1980) {
  42. $timearray['year'] = 1980;
  43. $timearray['mon'] = 1;
  44. $timearray['mday'] = 1;
  45. $timearray['hours'] = 0;
  46. $timearray['minutes'] = 0;
  47. $timearray['seconds'] = 0;
  48. }
  49. return (($timearray['year'] - 1980) << 25)
  50. | ($timearray['mon'] << 21)
  51. | ($timearray['mday'] << 16)
  52. | ($timearray['hours'] << 11)
  53. | ($timearray['minutes'] << 5)
  54. | ($timearray['seconds'] >> 1);
  55. }
  56. var $old_offset = 0;
  57. private function addFile($data, $filename, $time = 0)
  58. {
  59. $filename = str_replace('\\', '/', $filename);
  60. $dtime = dechex($this->unix2DosTime($time));
  61. $hexdtime = '\x' . $dtime[6] . $dtime[7]
  62. . '\x' . $dtime[4] . $dtime[5]
  63. . '\x' . $dtime[2] . $dtime[3]
  64. . '\x' . $dtime[0] . $dtime[1];
  65. eval('$hexdtime = "' . $hexdtime . '";');
  66. $fr = "\x50\x4b\x03\x04";
  67. $fr .= "\x14\x00";
  68. $fr .= "\x00\x00";
  69. $fr .= "\x08\x00";
  70. $fr .= $hexdtime;
  71. $unc_len = strlen($data);
  72. $crc = crc32($data);
  73. $zdata = gzcompress($data);
  74. $c_len = strlen($zdata);
  75. $zdata = substr(substr($zdata, 0, strlen($zdata) - 4), 2);
  76. $fr .= pack('V', $crc);
  77. $fr .= pack('V', $c_len);
  78. $fr .= pack('V', $unc_len);
  79. $fr .= pack('v', strlen($filename));
  80. $fr .= pack('v', 0);
  81. $fr .= $filename;
  82. $fr .= $zdata;
  83. $fr .= pack('V', $crc);
  84. $fr .= pack('V', $c_len);
  85. $fr .= pack('V', $unc_len);
  86. $this->datasec[] = $fr;
  87. $new_offset = strlen(implode('', $this->datasec));
  88. $cdrec = "\x50\x4b\x01\x02";
  89. $cdrec .= "\x00\x00";
  90. $cdrec .= "\x14\x00";
  91. $cdrec .= "\x00\x00";
  92. $cdrec .= "\x08\x00";
  93. $cdrec .= $hexdtime;
  94. $cdrec .= pack('V', $crc);
  95. $cdrec .= pack('V', $c_len);
  96. $cdrec .= pack('V', $unc_len);
  97. $cdrec .= pack('v', strlen($filename));
  98. $cdrec .= pack('v', 0);
  99. $cdrec .= pack('v', 0);
  100. $cdrec .= pack('v', 0);
  101. $cdrec .= pack('v', 0);
  102. $cdrec .= pack('V', 32);
  103. $cdrec .= pack('V', $this->old_offset);
  104. $this->old_offset = $new_offset;
  105. $cdrec .= $filename;
  106. $this->ctrl_dir[] = $cdrec;
  107. }
  108. var $eof_ctrl_dir = "\x50\x4b\x05\x06\x00\x00\x00\x00";
  109. private function file()
  110. {
  111. $data = implode('', $this->datasec);
  112. $ctrldir = implode('', $this->ctrl_dir);
  113. return $data
  114. . $ctrldir
  115. . $this->eof_ctrl_dir
  116. . pack('v', sizeof($this->ctrl_dir))
  117. . pack('v', sizeof($this->ctrl_dir))
  118. . pack('V', strlen($ctrldir))
  119. . pack('V', strlen($data))
  120. . "\x00\x00";
  121. }
  122. // ------------------------------------------------------ //
  123. // #压缩到服务器
  124. //
  125. // $archive = new PHPZip();
  126. // $archive->Zip("需压缩的文件所在目录", "ZIP压缩文件名");
  127. // ------------------------------------------------------ //
  128. public function zip($dir, $saveName)
  129. {
  130. if (@!function_exists('gzcompress')) {
  131. return;
  132. }
  133. ob_end_clean();
  134. $filelist = $this->visitFile($dir);
  135. if (count($filelist) == 0) {
  136. return;
  137. }
  138. foreach ($filelist as $file) {
  139. if (!file_exists($file) || !is_file($file)) {
  140. continue;
  141. }
  142. $fd = fopen($file, "rb");
  143. $content = @fread($fd, filesize($file));
  144. fclose($fd);
  145. // 1.删除$dir的字符(./folder/file.txt删除./folder/)
  146. // 2.如果存在/就删除(/file.txt删除/)
  147. $file = substr($file, strlen($dir));
  148. if (substr($file, 0, 1) == "\\" || substr($file, 0, 1) == "/") {
  149. $file = substr($file, 1);
  150. }
  151. $this->addFile($content, $file);
  152. }
  153. $out = $this->file();
  154. $fp = fopen($saveName, "wb");
  155. fwrite($fp, $out, strlen($out));
  156. fclose($fp);
  157. }
  158. // ------------------------------------------------------ //
  159. // #压缩并直接下载
  160. //
  161. // $archive = new PHPZip();
  162. // $archive->ZipAndDownload("需压缩的文件所在目录");
  163. // ------------------------------------------------------ //
  164. public function ZipAndDownload($dir)
  165. {
  166. if (@!function_exists('gzcompress')) {
  167. return;
  168. }
  169. ob_end_clean();
  170. $filelist = $this->visitFile($dir);
  171. if (count($filelist) == 0) {
  172. return;
  173. }
  174. foreach ($filelist as $file) {
  175. if (!file_exists($file) || !is_file($file)) {
  176. continue;
  177. }
  178. $fd = fopen($file, "rb");
  179. $content = @fread($fd, filesize($file));
  180. fclose($fd);
  181. // 1.删除$dir的字符(./folder/file.txt删除./folder/)
  182. // 2.如果存在/就删除(/file.txt删除/)
  183. $file = substr($file, strlen($dir));
  184. if (substr($file, 0, 1) == "\\" || substr($file, 0, 1) == "/") {
  185. $file = substr($file, 1);
  186. }
  187. $this->addFile($content, $file);
  188. }
  189. $out = $this->file();
  190. @header('Content-Encoding: none');
  191. @header('Content-Type: application/zip');
  192. @header('Content-Disposition: attachment ; filename=Farticle' . date("YmdHis", time()) . '.zip');
  193. @header('Pragma: no-cache');
  194. @header('Expires: 0');
  195. print($out);
  196. }
  197. /**********************************************************
  198. * 解压部分
  199. **********************************************************/
  200. // ------------------------------------------------------ //
  201. // ReadCentralDir($zip, $zipfile)
  202. // $zip是经过@fopen($zipfile, 'rb')打开的
  203. // $zipfile是zip文件的路径
  204. // ------------------------------------------------------ //
  205. private function ReadCentralDir($zip, $zipfile)
  206. {
  207. $size = filesize($zipfile);
  208. $max_size = ($size < 277) ? $size : 277;
  209. @fseek($zip, $size - $max_size);
  210. $pos = ftell($zip);
  211. $bytes = 0x00000000;
  212. while ($pos < $size) {
  213. $byte = @fread($zip, 1);
  214. $bytes = ($bytes << 8) | Ord($byte);
  215. $pos++;
  216. // 如果不是windows服务器,则要以504b0506作为对比
  217. if (strrpos(strtolower(PHP_OS), "win") === FALSE && substr(dechex($bytes), -8, 8) == '504b0506') {
  218. break;
  219. } // 如果是windows服务器,则要以0x504b0506作为对比
  220. elseif ($bytes == 0x504b0506) {
  221. break;
  222. }
  223. }
  224. $data = unpack('vdisk/vdisk_start/vdisk_entries/ventries/Vsize/Voffset/vcomment_size', fread($zip, 18));
  225. $centd['comment'] = ($data['comment_size'] != 0) ? fread($zip, $data['comment_size']) : ''; // 注释
  226. $centd['entries'] = $data['entries'];
  227. $centd['disk_entries'] = $data['disk_entries'];
  228. $centd['offset'] = $data['offset'];
  229. $centd['disk_start'] = $data['disk_start'];
  230. $centd['size'] = $data['size'];
  231. $centd['disk'] = $data['disk'];
  232. return $centd;
  233. }
  234. private function ReadCentralFileHeaders($zip)
  235. {
  236. $binary_data = fread($zip, 46);
  237. $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);
  238. $header['filename'] = ($header['filename_len'] != 0) ? fread($zip, $header['filename_len']) : '';
  239. $header['extra'] = ($header['extra_len'] != 0) ? fread($zip, $header['extra_len']) : '';
  240. $header['comment'] = ($header['comment_len'] != 0) ? fread($zip, $header['comment_len']) : '';
  241. if ($header['mdate'] && $header['mtime']) {
  242. $hour = ($header['mtime'] & 0xF800) >> 11;
  243. $minute = ($header['mtime'] & 0x07E0) >> 5;
  244. $seconde = ($header['mtime'] & 0x001F) * 2;
  245. $year = (($header['mdate'] & 0xFE00) >> 9) + 1980;
  246. $month = ($header['mdate'] & 0x01E0) >> 5;
  247. $day = $header['mdate'] & 0x001F;
  248. $header['mtime'] = mktime($hour, $minute, $seconde, $month, $day, $year);
  249. } else {
  250. $header['mtime'] = time();
  251. }
  252. $header['stored_filename'] = $header['filename'];
  253. $header['status'] = 'ok';
  254. if (substr($header['filename'], -1) == '/') {
  255. $header['external'] = 0x41FF0010;
  256. } // 判断是否文件夹
  257. return $header;
  258. }
  259. private function ReadFileHeader($zip)
  260. {
  261. $binary_data = fread($zip, 30);
  262. $data = unpack('vchk/vid/vversion/vflag/vcompression/vmtime/vmdate/Vcrc/Vcompressed_size/Vsize/vfilename_len/vextra_len', $binary_data);
  263. $header['filename'] = fread($zip, $data['filename_len']);
  264. $header['extra'] = ($data['extra_len'] != 0) ? fread($zip, $data['extra_len']) : '';
  265. $header['compression'] = $data['compression'];
  266. $header['size'] = $data['size'];
  267. $header['compressed_size'] = $data['compressed_size'];
  268. $header['crc'] = $data['crc'];
  269. $header['flag'] = $data['flag'];
  270. $header['mdate'] = $data['mdate'];
  271. $header['mtime'] = $data['mtime'];
  272. if ($header['mdate'] && $header['mtime']) {
  273. $hour = ($header['mtime'] & 0xF800) >> 11;
  274. $minute = ($header['mtime'] & 0x07E0) >> 5;
  275. $seconde = ($header['mtime'] & 0x001F) * 2;
  276. $year = (($header['mdate'] & 0xFE00) >> 9) + 1980;
  277. $month = ($header['mdate'] & 0x01E0) >> 5;
  278. $day = $header['mdate'] & 0x001F;
  279. $header['mtime'] = mktime($hour, $minute, $seconde, $month, $day, $year);
  280. } else {
  281. $header['mtime'] = time();
  282. }
  283. $header['stored_filename'] = $header['filename'];
  284. $header['status'] = "ok";
  285. return $header;
  286. }
  287. private function ExtractFile($header, $to, $zip)
  288. {
  289. $header = $this->readfileheader($zip);
  290. if (substr($to, -1) != "/") {
  291. $to .= "/";
  292. }
  293. if (!@is_dir($to)) {
  294. @mkdir($to, 0777);
  295. }
  296. $pth = explode("/", dirname($header['filename']));
  297. $pthss = '';
  298. for ($i = 0; isset($pth[$i]); $i++) {
  299. if (!$pth[$i]) {
  300. continue;
  301. }
  302. $pthss .= $pth[$i] . "/";
  303. if (!is_dir($to . $pthss)) {
  304. @mkdir($to . $pthss, 0777);
  305. }
  306. }
  307. if (!isset($header['external']) || ($header['external'] != 0x41FF0010 && $header['external'] != 16)) {
  308. if ($header['compression'] == 0) {
  309. $fp = @fopen($to . $header['filename'], 'wb');
  310. if (!$fp) {
  311. return (-1);
  312. }
  313. $size = $header['compressed_size'];
  314. while ($size != 0) {
  315. $read_size = ($size < 2048 ? $size : 2048);
  316. $buffer = fread($zip, $read_size);
  317. $binary_data = pack('a' . $read_size, $buffer);
  318. @fwrite($fp, $binary_data, $read_size);
  319. $size -= $read_size;
  320. }
  321. fclose($fp);
  322. touch($to . $header['filename'], $header['mtime']);
  323. } else {
  324. $fp = @fopen($to . $header['filename'] . '.gz', 'wb');
  325. if (!$fp) {
  326. return (-1);
  327. }
  328. $binary_data = pack('va1a1Va1a1', 0x8b1f, Chr($header['compression']), Chr(0x00), time(), Chr(0x00), Chr(3));
  329. fwrite($fp, $binary_data, 10);
  330. $size = $header['compressed_size'];
  331. while ($size != 0) {
  332. $read_size = ($size < 1024 ? $size : 1024);
  333. $buffer = fread($zip, $read_size);
  334. $binary_data = pack('a' . $read_size, $buffer);
  335. @fwrite($fp, $binary_data, $read_size);
  336. $size -= $read_size;
  337. }
  338. $binary_data = pack('VV', $header['crc'], $header['size']);
  339. fwrite($fp, $binary_data, 8);
  340. fclose($fp);
  341. $gzp = @gzopen($to . $header['filename'] . '.gz', 'rb') or die("Cette archive est compress!");
  342. if (!$gzp) {
  343. return (-2);
  344. }
  345. $fp = @fopen($to . $header['filename'], 'wb');
  346. if (!$fp) {
  347. return (-1);
  348. }
  349. $size = $header['size'];
  350. while ($size != 0) {
  351. $read_size = ($size < 2048 ? $size : 2048);
  352. $buffer = gzread($gzp, $read_size);
  353. $binary_data = pack('a' . $read_size, $buffer);
  354. @fwrite($fp, $binary_data, $read_size);
  355. $size -= $read_size;
  356. }
  357. fclose($fp);
  358. gzclose($gzp);
  359. touch($to . $header['filename'], $header['mtime']);
  360. @unlink($to . $header['filename'] . '.gz');
  361. }
  362. }
  363. return true;
  364. }
  365. // ------------------------------------------------------ //
  366. // #解压文件
  367. //
  368. // $archive = new PHPZip();
  369. // $zipfile = "ZIP压缩文件名";
  370. // $savepath = "解压缩目录名";
  371. // $zipfile = $unzipfile;
  372. // $savepath = $unziptarget;
  373. // $array = $archive->GetZipInnerFilesInfo($zipfile);
  374. // $filecount = 0;
  375. // $dircount = 0;
  376. // $failfiles = array();
  377. // set_time_limit(0); // 修改为不限制超时时间(默认为30秒)
  378. //
  379. // for($i=0; $i<count($array); $i++) {
  380. // if($array[$i][folder] == 0){
  381. // if($archive->unZip($zipfile, $savepath, $i) > 0){
  382. // $filecount++;
  383. // }else{
  384. // $failfiles[] = $array[$i][filename];
  385. // }
  386. // }else{
  387. // $dircount++;
  388. // }
  389. // }
  390. // set_time_limit(30);
  391. //printf("文件夹:%d&nbsp;&nbsp;&nbsp;&nbsp;解压文件:%d&nbsp;&nbsp;&nbsp;&nbsp;失败:%d<br>\r\n", $dircount, $filecount, count($failfiles));
  392. //if(count($failfiles) > 0){
  393. // foreach($failfiles as $file){
  394. // printf("&middot;%s<br>\r\n", $file);
  395. // }
  396. //}
  397. // ------------------------------------------------------ //
  398. public function unzip($zipfile, $to, $index = Array(-1))
  399. {
  400. $ok = 0;
  401. $zip = @fopen($zipfile, 'rb');
  402. if (!$zip) {
  403. return (-1);
  404. }
  405. $cdir = $this->ReadCentralDir($zip, $zipfile);
  406. $pos_entry = $cdir['offset'];
  407. if (!is_array($index)) {
  408. $index = array($index);
  409. }
  410. for ($i = 0; isset($index[$i]) && $index[$i]; $i++) {
  411. if (intval($index[$i]) != $index[$i] || $index[$i] > $cdir['entries']) {
  412. return (-1);
  413. }
  414. }
  415. for ($i = 0; $i < $cdir['entries']; $i++) {
  416. @fseek($zip, $pos_entry);
  417. $header = $this->ReadCentralFileHeaders($zip);
  418. $header['index'] = $i;
  419. $pos_entry = ftell($zip);
  420. @rewind($zip);
  421. fseek($zip, $header['offset']);
  422. if (in_array("-1", $index) || in_array($i, $index)) {
  423. $stat[$header['filename']] = $this->ExtractFile($header, $to, $zip);
  424. }
  425. }
  426. fclose($zip);
  427. return $stat;
  428. }
  429. /**********************************************************
  430. * 其它部分
  431. **********************************************************/
  432. // ------------------------------------------------------ //
  433. // #获取被压缩文件的信息
  434. //
  435. // $archive = new PHPZip();
  436. // $array = $archive->GetZipInnerFilesInfo(ZIP压缩文件名);
  437. // for($i=0; $i<count($array); $i++) {
  438. // printf("<b>&middot;%s</b><br>\r\n", $array[$i][filename]);
  439. // foreach($array[$i] as $key => $value)
  440. // printf("%s => %s<br>\r\n", $key, $value);
  441. // print "\r\n<p>------------------------------------<p>\r\n\r\n";
  442. // }
  443. // ------------------------------------------------------ //
  444. public function GetZipInnerFilesInfo($zipfile)
  445. {
  446. $zip = @fopen($zipfile, 'rb');
  447. if (!$zip) {
  448. return (0);
  449. }
  450. $centd = $this->ReadCentralDir($zip, $zipfile);
  451. @rewind($zip);
  452. @fseek($zip, $centd['offset']);
  453. $ret = array();
  454. for ($i = 0; $i < $centd['entries']; $i++) {
  455. $header = $this->ReadCentralFileHeaders($zip);
  456. $header['index'] = $i;
  457. $info = array(
  458. 'filename' => $header['filename'], // 文件名
  459. 'stored_filename' => $header['stored_filename'], // 压缩后文件名
  460. 'size' => $header['size'], // 大小
  461. 'compressed_size' => $header['compressed_size'], // 压缩后大小
  462. 'crc' => strtoupper(dechex($header['crc'])), // CRC32
  463. 'mtime' => date("Y-m-d H:i:s", $header['mtime']), // 文件修改时间
  464. 'comment' => $header['comment'], // 注释
  465. 'folder' => ($header['external'] == 0x41FF0010 || $header['external'] == 16) ? 1 : 0, // 是否为文件夹
  466. 'index' => $header['index'], // 文件索引
  467. 'status' => $header['status'] // 状态
  468. );
  469. $ret[] = $info;
  470. unset($header);
  471. }
  472. fclose($zip);
  473. return $ret;
  474. }
  475. // ------------------------------------------------------ //
  476. // #获取压缩文件的注释
  477. //
  478. // $archive = new PHPZip();
  479. // echo $archive->GetZipComment(ZIP压缩文件名);
  480. // ------------------------------------------------------ //
  481. public function GetZipComment($zipfile)
  482. {
  483. $zip = @fopen($zipfile, 'rb');
  484. if (!$zip) {
  485. return (0);
  486. }
  487. $centd = $this->ReadCentralDir($zip, $zipfile);
  488. fclose($zip);
  489. return $centd[comment];
  490. }
  491. }