国内流行的内容管理系统(CMS)多端全媒体解决方案 https://www.dedebiz.com
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.

522 lines
19KB

  1. <?php
  2. /**
  3. * Zip压缩类
  4. *
  5. * @version $Id: zip.class.php 1 15:21 2010年7月5日Z tianya $
  6. * @package DedeBIZ.Libraries
  7. * @copyright Copyright (c) 2020, DedeBIZ.COM
  8. * @license https://www.dedebiz.com/license
  9. * @link https://www.dedebiz.com
  10. */
  11. class zip
  12. {
  13. var $datasec, $ctrl_dir = array();
  14. var $eof_ctrl_dir = "\x50\x4b\x05\x06\x00\x00\x00\x00";
  15. var $old_offset = 0;
  16. var $dirs = array(".");
  17. /**
  18. * 获取zip文件中的文件列表
  19. *
  20. * @access public
  21. * @param string $zip_name zip文件名
  22. * @return array
  23. */
  24. function get_List($zip_name)
  25. {
  26. $ret = '';
  27. $zip = @fopen($zip_name, 'rb');
  28. if (!$zip) {
  29. return (0);
  30. }
  31. $centd = $this->ReadCentralDir($zip, $zip_name);
  32. @rewind($zip);
  33. @fseek($zip, $centd['offset']);
  34. for ($i = 0; $i < $centd['entries']; $i++) {
  35. $header = $this->ReadCentralFileHeaders($zip);
  36. $header['index'] = $i;
  37. $info['filename'] = $header['filename'];
  38. $info['stored_filename'] = $header['stored_filename'];
  39. $info['size'] = $header['size'];
  40. $info['compressed_size'] = $header['compressed_size'];
  41. $info['crc'] = strtoupper(dechex($header['crc']));
  42. $info['mtime'] = $header['mtime'];
  43. $info['comment'] = $header['comment'];
  44. $info['folder'] = ($header['external'] == 0x41FF0010 || $header['external'] == 16) ? 1 : 0;
  45. $info['index'] = $header['index'];
  46. $info['status'] = $header['status'];
  47. $ret[] = $info;
  48. unset($header);
  49. }
  50. return $ret;
  51. }
  52. /**
  53. * 增加文件到压缩文件
  54. *
  55. * @access public
  56. * @param string $files 需要增加的文件列表,可以是字符串也可以是数组
  57. * @param string $compact 压缩文件名称
  58. * @return array 压缩文件信息
  59. */
  60. function Add($files, $compact)
  61. {
  62. if (!is_array($files[0])) {
  63. $files = array($files);
  64. }
  65. for ($i = 0; $files[$i]; $i++) {
  66. $fn = $files[$i];
  67. if (!in_Array(dirname($fn[0]), $this->dirs)) {
  68. $this->add_Dir(dirname($fn[0]));
  69. }
  70. if (basename($fn[0])) {
  71. $ret[basename($fn[0])] = $this->add_File($fn[1], $fn[0], $compact);
  72. }
  73. }
  74. return $ret;
  75. }
  76. /**
  77. * 获取文件,获取后可以让其进行下载
  78. *
  79. * @access public
  80. * @return void
  81. */
  82. function get_file()
  83. {
  84. $data = implode('', $this->datasec);
  85. $ctrldir = implode('', $this->ctrl_dir);
  86. return $data . $ctrldir . $this->eof_ctrl_dir .
  87. pack('v', sizeof($this->ctrl_dir)) . pack('v', sizeof($this->ctrl_dir)) .
  88. pack('V', strlen($ctrldir)) . pack('V', strlen($data)) . "\x00\x00";
  89. }
  90. /**
  91. * 增加文件目录
  92. *
  93. * @access public
  94. * @param string $name 目录名称
  95. * @return void
  96. */
  97. function add_dir($name)
  98. {
  99. $name = str_replace("\\", "/", $name);
  100. $fr = "\x50\x4b\x03\x04\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00";
  101. $fr .= pack("V", 0) . pack("V", 0) . pack("V", 0) . pack("v", strlen($name));
  102. $fr .= pack("v", 0) . $name . pack("V", 0) . pack("V", 0) . pack("V", 0);
  103. $this->datasec[] = $fr;
  104. $new_offset = strlen(implode("", $this->datasec));
  105. $cdrec = "\x50\x4b\x01\x02\x00\x00\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00";
  106. $cdrec .= pack("V", 0) . pack("V", 0) . pack("V", 0) . pack("v", strlen($name));
  107. $cdrec .= pack("v", 0) . pack("v", 0) . pack("v", 0) . pack("v", 0);
  108. $ext = "\xff\xff\xff\xff";
  109. $cdrec .= pack("V", 16) . pack("V", $this->old_offset) . $name;
  110. $this->ctrl_dir[] = $cdrec;
  111. $this->old_offset = $new_offset;
  112. $this->dirs[] = $name;
  113. }
  114. /**
  115. * 编译指定的文件为zip文件(filename可以为文件数组array、目录dir或单个文件file)
  116. *
  117. * @access public
  118. * @param string $filename 文件名称
  119. * @param string $tozipfilename 压缩文件名称
  120. * @param string $ftype 压缩类型
  121. * @return int 影响文件数
  122. */
  123. function CompileZipFile($filename, $tozipfilename, $ftype = 'dir')
  124. {
  125. if (@function_exists('gzcompress')) {
  126. if ($ftype == 'dir') {
  127. $filelist = $this->ListDirFiles($filename);
  128. } else if ($ftype == 'file') {
  129. $filelist[] = $filename;
  130. } else {
  131. $filelist = $filename;
  132. }
  133. $i = 0;
  134. if (count($filelist) > 0) {
  135. foreach ($filelist as $filename) {
  136. if (is_file($filename)) {
  137. $i++;
  138. $fd = fopen($filename, "r");
  139. if (filesize($filename) > 0) {
  140. $content = fread($fd, filesize($filename));
  141. } else {
  142. $content = ' ';
  143. }
  144. fclose($fd);
  145. //if (is_array($dir)) $filename = basename($filename);
  146. $this->add_File($content, $filename);
  147. }
  148. }
  149. $out = $this->get_file();
  150. $fp = fopen($tozipfilename, "w");
  151. fwrite($fp, $out, strlen($out));
  152. fclose($fp);
  153. }
  154. return $i;
  155. } else {
  156. return 0;
  157. }
  158. }
  159. /**
  160. * 读取某文件夹的所有文件
  161. *
  162. * @access public
  163. * @param string $dirname 目录名称
  164. * @return mix 如果失败则返回false
  165. */
  166. function ListDirFiles($dirname)
  167. {
  168. $files = array();
  169. if (is_dir($dirname)) {
  170. $fh = opendir($dirname);
  171. while (($file = readdir($fh)) !== false) {
  172. if (strcmp($file, '.') == 0 || strcmp($file, '..') == 0) {
  173. continue;
  174. }
  175. $filepath = $dirname . '/' . $file;
  176. if (is_dir($filepath)) {
  177. $files = array_merge($files, $this->ListDirFiles($filepath));
  178. } else {
  179. array_push($files, $filepath);
  180. }
  181. }
  182. closedir($fh);
  183. } else {
  184. $files = false;
  185. }
  186. return $files;
  187. }
  188. /**
  189. * 增加文件
  190. *
  191. * @access public
  192. * @param string $data 数据
  193. * @param string $name 名称
  194. * @param string $compact 压缩
  195. * @return string
  196. */
  197. function add_File($data, $name, $compact = 1)
  198. {
  199. $name = str_replace('\\', '/', $name);
  200. $dtime = dechex($this->DosTime());
  201. $hexdtime = '\x' . $dtime[6] . $dtime[7] . '\x' . $dtime[4] . $dtime[5]
  202. . '\x' . $dtime[2] . $dtime[3] . '\x' . $dtime[0] . $dtime[1];
  203. eval('$hexdtime = "' . $hexdtime . '";');
  204. if ($compact)
  205. $fr = "\x50\x4b\x03\x04\x14\x00\x00\x00\x08\x00" . $hexdtime;
  206. else {
  207. $fr = "\x50\x4b\x03\x04\x0a\x00\x00\x00\x00\x00" . $hexdtime;
  208. }
  209. $unc_len = strlen($data);
  210. $crc = crc32($data);
  211. if ($compact) {
  212. $zdata = gzcompress($data);
  213. $c_len = strlen($zdata);
  214. $zdata = substr(substr($zdata, 0, strlen($zdata) - 4), 2);
  215. } else {
  216. $zdata = $data;
  217. }
  218. $c_len = strlen($zdata);
  219. $fr .= pack('V', $crc) . pack('V', $c_len) . pack('V', $unc_len);
  220. $fr .= pack('v', strlen($name)) . pack('v', 0) . $name . $zdata;
  221. $fr .= pack('V', $crc) . pack('V', $c_len) . pack('V', $unc_len);
  222. $this->datasec[] = $fr;
  223. $new_offset = strlen(implode('', $this->datasec));
  224. if ($compact) {
  225. $cdrec = "\x50\x4b\x01\x02\x00\x00\x14\x00\x00\x00\x08\x00";
  226. } else {
  227. $cdrec = "\x50\x4b\x01\x02\x14\x00\x0a\x00\x00\x00\x00\x00";
  228. }
  229. $cdrec .= $hexdtime . pack('V', $crc) . pack('V', $c_len) . pack('V', $unc_len);
  230. $cdrec .= pack('v', strlen($name)) . pack('v', 0) . pack('v', 0);
  231. $cdrec .= pack('v', 0) . pack('v', 0) . pack('V', 32);
  232. $cdrec .= pack('V', $this->old_offset);
  233. $this->old_offset = $new_offset;
  234. $cdrec .= $name;
  235. $this->ctrl_dir[] = $cdrec;
  236. return true;
  237. }
  238. /**
  239. * 返回时间
  240. *
  241. * @access public
  242. * @return int
  243. */
  244. function DosTime()
  245. {
  246. $timearray = getdate();
  247. if ($timearray['year'] < 1980) {
  248. $timearray['year'] = 1980;
  249. $timearray['mon'] = 1;
  250. $timearray['mday'] = 1;
  251. $timearray['hours'] = 0;
  252. $timearray['minutes'] = 0;
  253. $timearray['seconds'] = 0;
  254. }
  255. return (($timearray['year'] - 1980) << 25) | ($timearray['mon'] << 21) | ($timearray['mday'] << 16) | ($timearray['hours'] << 11) |
  256. ($timearray['minutes'] << 5) | ($timearray['seconds'] >> 1);
  257. }
  258. /**
  259. * 解压整个压缩包
  260. * 直接用 Extract 会有路径问题,本函数先从列表中获得文件信息并创建好所有目录然后才运行 Extract
  261. *
  262. * @access public
  263. * @param string $zn zip文件名称
  264. * @param string $to 解压到的目录地址
  265. * @return string
  266. */
  267. function ExtractAll($zn, $to)
  268. {
  269. if (substr($to, -1) != "/") {
  270. $to .= "/";
  271. }
  272. $files = $this->get_List($zn);
  273. $cn = count($files);
  274. if (is_array($files)) {
  275. for ($i = 0; $i < $cn; $i++) {
  276. if ($files[$i]['folder'] == 1) {
  277. @mkdir($to . $files[$i]['filename'], $GLOBALS['cfg_dir_purview']);
  278. @chmod($to . $files[$i]['filename'], $GLOBALS['cfg_dir_purview']);
  279. }
  280. }
  281. }
  282. $this->Extract($zn, $to);
  283. }
  284. /**
  285. * 解压单个文件
  286. *
  287. * @access public
  288. * @param string $zn zip文件名称
  289. * @param string $to 解压到的目录地址
  290. * @return string
  291. */
  292. function Extract($zn, $to, $index = array(-1))
  293. {
  294. $ok = 0;
  295. $zip = @fopen($zn, 'rb');
  296. if (!$zip) {
  297. return (-1);
  298. }
  299. $cdir = $this->ReadCentralDir($zip, $zn);
  300. $pos_entry = $cdir['offset'];
  301. if (!is_array($index)) {
  302. $index = array($index);
  303. }
  304. for ($i = 0; isset($index[$i]); $i++) {
  305. if (intval($index[$i]) != $index[$i] || $index[$i] > $cdir['entries']) {
  306. return (-1);
  307. }
  308. }
  309. for ($i = 0; $i < $cdir['entries']; $i++) {
  310. @fseek($zip, $pos_entry);
  311. $header = $this->ReadCentralFileHeaders($zip);
  312. $header['index'] = $i;
  313. $pos_entry = ftell($zip);
  314. @rewind($zip);
  315. fseek($zip, $header['offset']);
  316. if (in_array("-1", $index) || in_array($i, $index)) {
  317. $stat[$header['filename']] = $this->ExtractFile($header, $to, $zip);
  318. }
  319. }
  320. fclose($zip);
  321. return $stat;
  322. }
  323. function ReadFileHeader($zip)
  324. {
  325. $binary_data = fread($zip, 30);
  326. $data = unpack('vchk/vid/vversion/vflag/vcompression/vmtime/vmdate/Vcrc/Vcompressed_size/Vsize/vfilename_len/vextra_len', $binary_data);
  327. $header['filename'] = fread($zip, $data['filename_len']);
  328. if ($data['extra_len'] != 0) {
  329. $header['extra'] = fread($zip, $data['extra_len']);
  330. } else {
  331. $header['extra'] = '';
  332. }
  333. $header['compression'] = $data['compression'];
  334. $header['size'] = $data['size'];
  335. $header['compressed_size'] = $data['compressed_size'];
  336. $header['crc'] = $data['crc'];
  337. $header['flag'] = $data['flag'];
  338. $header['mdate'] = $data['mdate'];
  339. $header['mtime'] = $data['mtime'];
  340. if ($header['mdate'] && $header['mtime']) {
  341. $hour = ($header['mtime'] & 0xF800) >> 11;
  342. $minute = ($header['mtime'] & 0x07E0) >> 5;
  343. $seconde = ($header['mtime'] & 0x001F) * 2;
  344. $year = (($header['mdate'] & 0xFE00) >> 9) + 1980;
  345. $month = ($header['mdate'] & 0x01E0) >> 5;
  346. $day = $header['mdate'] & 0x001F;
  347. $header['mtime'] = mktime($hour, $minute, $seconde, $month, $day, $year);
  348. } else {
  349. $header['mtime'] = time();
  350. }
  351. $header['stored_filename'] = $header['filename'];
  352. $header['status'] = "ok";
  353. return $header;
  354. }
  355. function ReadCentralFileHeaders($zip)
  356. {
  357. $binary_data = fread($zip, 46);
  358. $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);
  359. if ($header['filename_len'] != 0) {
  360. $header['filename'] = fread($zip, $header['filename_len']);
  361. } else {
  362. $header['filename'] = '';
  363. }
  364. if ($header['extra_len'] != 0) {
  365. $header['extra'] = fread($zip, $header['extra_len']);
  366. } else {
  367. $header['extra'] = '';
  368. }
  369. if ($header['comment_len'] != 0) {
  370. $header['comment'] = fread($zip, $header['comment_len']);
  371. } else {
  372. $header['comment'] = '';
  373. }
  374. if ($header['mdate'] && $header['mtime']) {
  375. $hour = ($header['mtime'] & 0xF800) >> 11;
  376. $minute = ($header['mtime'] & 0x07E0) >> 5;
  377. $seconde = ($header['mtime'] & 0x001F) * 2;
  378. $year = (($header['mdate'] & 0xFE00) >> 9) + 1980;
  379. $month = ($header['mdate'] & 0x01E0) >> 5;
  380. $day = $header['mdate'] & 0x001F;
  381. $header['mtime'] = mktime($hour, $minute, $seconde, $month, $day, $year);
  382. } else {
  383. $header['mtime'] = time();
  384. }
  385. $header['stored_filename'] = $header['filename'];
  386. $header['status'] = 'ok';
  387. if (substr($header['filename'], -1) == '/') {
  388. $header['external'] = 0x41FF0010;
  389. }
  390. return $header;
  391. }
  392. function ReadCentralDir($zip, $zip_name)
  393. {
  394. $size = filesize($zip_name);
  395. if ($size < 277) {
  396. $maximum_size = $size;
  397. } else {
  398. $maximum_size = 277;
  399. }
  400. @fseek($zip, $size - $maximum_size);
  401. $pos = ftell($zip);
  402. $bytes = 0x00000000;
  403. while ($pos < $size) {
  404. $byte = @fread($zip, 1);
  405. $bytes = ($bytes << 8) | Ord($byte);
  406. if ($bytes == 0x504b0506) {
  407. $pos++;
  408. break;
  409. }
  410. $pos++;
  411. }
  412. $data = @unpack('vdisk/vdisk_start/vdisk_entries/ventries/Vsize/Voffset/vcomment_size', fread($zip, 18));
  413. if ($data['comment_size'] != 0) {
  414. $centd['comment'] = fread($zip, $data['comment_size']);
  415. } else {
  416. $centd['comment'] = '';
  417. $centd['entries'] = $data['entries'];
  418. }
  419. $centd['disk_entries'] = $data['disk_entries'];
  420. $centd['offset'] = $data['offset'];
  421. $centd['disk_start'] = $data['disk_start'];
  422. $centd['size'] = $data['size'];
  423. $centd['disk'] = $data['disk'];
  424. return $centd;
  425. }
  426. function ExtractFile($header, $to, $zip)
  427. {
  428. $header = $this->readfileheader($zip);
  429. $header['external'] = (!isset($header['external']) ? 0 : $header['external']);
  430. if (substr($to, -1) != "/") {
  431. $to .= "/";
  432. }
  433. if (!@is_dir($to)) {
  434. @mkdir($to, $GLOBALS['cfg_dir_purview']);
  435. }
  436. if (!($header['external'] == 0x41FF0010) && !($header['external'] == 16)) {
  437. if ($header['compression'] == 0) {
  438. $fp = @fopen($to . $header['filename'], 'wb');
  439. if (!$fp) {
  440. return (-1);
  441. }
  442. $size = $header['compressed_size'];
  443. while ($size != 0) {
  444. $read_size = ($size < 2048 ? $size : 2048);
  445. $buffer = fread($zip, $read_size);
  446. $binary_data = pack('a' . $read_size, $buffer);
  447. @fwrite($fp, $binary_data, $read_size);
  448. $size -= $read_size;
  449. }
  450. fclose($fp);
  451. touch($to . $header['filename'], $header['mtime']);
  452. } else {
  453. $fp = @fopen($to . $header['filename'] . '.gz', 'wb');
  454. if (!$fp) {
  455. return (-1);
  456. }
  457. $binary_data = pack(
  458. 'va1a1Va1a1',
  459. 0x8b1f,
  460. Chr($header['compression']),
  461. Chr(0x00),
  462. time(),
  463. Chr(0x00),
  464. Chr(3)
  465. );
  466. fwrite($fp, $binary_data, 10);
  467. $size = $header['compressed_size'];
  468. while ($size != 0) {
  469. $read_size = ($size < 1024 ? $size : 1024);
  470. $buffer = fread($zip, $read_size);
  471. $binary_data = pack('a' . $read_size, $buffer);
  472. @fwrite($fp, $binary_data, $read_size);
  473. $size -= $read_size;
  474. }
  475. $binary_data = pack('VV', $header['crc'], $header['size']);
  476. fwrite($fp, $binary_data, 8);
  477. fclose($fp);
  478. $gzp = @gzopen($to . $header['filename'] . '.gz', 'rb') or die("Cette archive est compress");
  479. if (!$gzp) {
  480. return (-2);
  481. }
  482. $fp = @fopen($to . $header['filename'], 'wb');
  483. if (!$fp) {
  484. return (-1);
  485. }
  486. $size = $header['size'];
  487. while ($size != 0) {
  488. $read_size = ($size < 2048 ? $size : 2048);
  489. $buffer = gzread($gzp, $read_size);
  490. $binary_data = pack('a' . $read_size, $buffer);
  491. @fwrite($fp, $binary_data, $read_size);
  492. $size -= $read_size;
  493. }
  494. fclose($fp);
  495. gzclose($gzp);
  496. touch($to . $header['filename'], $header['mtime']);
  497. @unlink($to . $header['filename'] . '.gz');
  498. }
  499. }
  500. return true;
  501. }
  502. }