Save.php 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592
  1. <?php namespace Upload\Lib;
  2. use Dever;
  3. class Save
  4. {
  5. private $config;
  6. private $cate_id;
  7. private $group_id;
  8. private $user_id;
  9. private $ext = [
  10. 1 => 'jpg,png,gif,webp,jpeg',
  11. 2 => 'mp3,m4a' ,
  12. 3 => 'video,flv,mp4,webm,mov',
  13. 4 => 'doc,xls,xlsx,docx',
  14. 5 => 'pdf',
  15. 6 => 'rar,zip',
  16. 7 => 'cer,pfx,pem',
  17. 8 => 'exe,msi',
  18. ];
  19. private $type = 1;
  20. public function init($id, $cate_id = 1, $group_id = false, $user_id = false)
  21. {
  22. $this->config = Dever::db('rule', 'upload')->find($id);
  23. if (!$this->config) {
  24. Dever::error('上传规则错误');
  25. }
  26. $this->config['save'] = Dever::db('save', 'upload')->find($this->config['save_id']);
  27. if (!$this->config['save']) {
  28. Dever::error('存储位置错误');
  29. }
  30. $this->cate_id = $cate_id ? $cate_id : 1;
  31. $this->group_id = $group_id;
  32. $this->user_id = $user_id;
  33. return $this;
  34. }
  35. public function get($id)
  36. {
  37. $this->init($id);
  38. $result = [];
  39. $result['id'] = $id;
  40. $result['chunkSize'] = $this->config['chunk'];
  41. $result['path'] = $id;
  42. $result['size'] = $this->config['size'];
  43. $result['accept'] = '';
  44. $result['mine'] = [];
  45. $mine = $this->getExtByMine(false, true);
  46. $type = explode(',', $this->config['type']);
  47. foreach ($type as $v) {
  48. if (isset($this->ext[$v])) {
  49. $result['accept'] .= '.' . str_replace(',', ',.', $this->ext[$v]);
  50. $temp = explode(',', $this->ext[$v]);
  51. foreach ($temp as $v2) {
  52. if (isset($mine[$v2])) {
  53. $result['mine'][] = $mine[$v2];
  54. }
  55. }
  56. }
  57. }
  58. $result['type'] = $this->config['save']['type'];
  59. $result['method'] = $this->config['save']['method'];
  60. if ($result['type'] > 1) {
  61. $result += Dever::load('tool', 'upload')->get($this->config['save'])->getInfo();
  62. }
  63. return $result;
  64. }
  65. public function act($source, $default_ext = '', $uid = false, $dest_name = '')
  66. {
  67. if (!$this->config) {
  68. Dever::error('上传规则错误');
  69. }
  70. $name = '';
  71. $ext = '';
  72. $size = 0;
  73. $method = '';
  74. $source_name = '';
  75. $chunk = [];
  76. if (is_array($source) && isset($source['tmp_name'])) {
  77. # 文件上传
  78. $type = 1;
  79. if ($source['name'] == 'blob' && $source['type'] == 'application/octet-stream') {
  80. $source['name'] = Dever::input('name');
  81. $source['type'] = Dever::input('type');
  82. }
  83. $total = Dever::input('chunks');
  84. if ($total > 1) {
  85. $chunk['size'] = $this->config['chunk'];
  86. $chunk['uid'] = Dever::input('uid');
  87. $chunk['timestamp'] = Dever::input('timestamp');
  88. $chunk['cur'] = Dever::input('chunk');
  89. $chunk['total'] = $total;
  90. if (!$chunk['uid'] || !$chunk['timestamp'] || !$chunk['cur'] || !$chunk['total']) {
  91. Dever::error('分片配置信息无效');
  92. }
  93. $name = $source_name = $source['name'] . $chunk['timestamp'] . $chunk['uid'];
  94. } else {
  95. $source_name = $source['name'];
  96. //$name = $source_name . uniqid(date('YmdHis'), true) . mt_rand(10000, 99999);
  97. $name = $source_name . $source['type'] . $source['size'];
  98. }
  99. $ext = $this->getExtByMine($source['type']);
  100. $size = $source['size'];
  101. $method = 'getimagesize';
  102. $source = $source['tmp_name'];
  103. } elseif (is_string($source)) {
  104. if (strstr($source, ';base64,')) {
  105. # base64编码
  106. $temp = explode(';base64,', $source);
  107. $ext = $this->getExtByMine(ltrim($temp[0], 'data:'));
  108. $source = str_replace(' ', '+', $temp[1]);
  109. $source = str_replace('=', '', $temp[1]);
  110. $source = base64_decode($source);
  111. $size = strlen($source);
  112. $size = round(($size - ($size/8)*2)/1024, 2);
  113. $method = 'getimagesizefromstring';
  114. $type = 2;
  115. } else {
  116. if (strstr($source, 'http')) {
  117. # http远程文件
  118. $name = $source;
  119. $type = 3;
  120. $ext = pathinfo($source, PATHINFO_EXTENSION);
  121. if ($this->config['save']['type'] == 1) {
  122. $content = Dever::curl($source)->log(false)->result();
  123. $source = Dever::file('tmp/' . sha1($name));
  124. file_put_contents($source, $content);
  125. }
  126. }
  127. if (is_file($source)) {
  128. # 本地文件
  129. $type = 1;
  130. $name = $source;
  131. $finfo = finfo_open(FILEINFO_MIME);
  132. $code = finfo_file($finfo, $source);
  133. finfo_close($finfo);
  134. $temp = explode(';', $code);
  135. $ext = $this->getExtByMine($temp[0]);
  136. $size = filesize($source);
  137. $method = 'getimagesize';
  138. }
  139. }
  140. } else {
  141. $source = false;
  142. }
  143. if (!$source) {
  144. Dever::error('源文件不存在');
  145. }
  146. if (!$ext) {
  147. $ext = $this->getExtByByte($source);
  148. }
  149. if (!$ext && $default_ext) {
  150. $ext = $default_ext;
  151. }
  152. $state = $this->check($ext, $size, $method, $source);
  153. if (is_string($state)) {
  154. if (isset($content)) {
  155. @unlink($source);
  156. }
  157. Dever::error($state);
  158. }
  159. if ($dest_name && strstr($dest_name, '/')) {
  160. $dest = $dest_name;
  161. } else {
  162. if ($dest_name) {
  163. $name = $dest_name;
  164. }
  165. $dest = $this->config['id'] . '/' . $this->getDest($name, $ext, $uid);
  166. $system = Dever::call("manage/common.system", [false, true, "upload/manage.getFileField"]);
  167. if ($system && isset($system['database'])) {
  168. $dest = $system['database'] . '/' . $dest;
  169. }
  170. }
  171. # type 1是文件复制 2是base64 3是远程文件复制
  172. $url = Dever::load('tool', 'upload')->get($this->config['save'])->upload($type, $source, $dest, $chunk, $this);
  173. $data = $this->up($source_name, $name, $dest, $this->config['size'], $this->config['width'] ?? 0, $this->config['height'] ?? 0);
  174. $data['url'] = $url . '?t=' . time();
  175. $data['type'] = $this->type;
  176. if (isset($content)) {
  177. @unlink($source);
  178. }
  179. return $data;
  180. }
  181. public function addFile($url, $source_name, $name, $dest, $size)
  182. {
  183. if ($this->config['save']['type'] > 1) {
  184. $url = Dever::load('tool', 'upload')->get($this->config['save'])->getUrl(3, $url, $this);
  185. if (isset($this->config['size']) && $this->config['size']) {
  186. $size = $this->config['size'];
  187. }
  188. }
  189. $data = $this->up($source_name, $name, $dest, $size, $this->config['width'] ?? 0, $this->config['height'] ?? 0);
  190. $data['url'] = $url;
  191. return $data;
  192. }
  193. private function up($source_name, $name, $dest, $size, $width = 0, $height = 0)
  194. {
  195. $file['rule_id'] = $this->config['id'];
  196. $file['name'] = $name;
  197. $data = $file;
  198. $data['source_name'] = $source_name;
  199. $data['file'] = $dest;
  200. $data['save_id'] = $this->config['save_id'];
  201. $data['size'] = $size;
  202. if ($width) {
  203. $data['width'] = $width;
  204. }
  205. if ($height) {
  206. $data['height'] = $height;
  207. }
  208. if ($this->cate_id) {
  209. $data['cate_id'] = $this->cate_id;
  210. }
  211. if ($this->group_id) {
  212. $data['group_id'] = $this->group_id;
  213. }
  214. if ($this->user_id) {
  215. $data['user_id'] = $this->user_id;
  216. }
  217. $data['id'] = Dever::db('file', 'upload')->up($file, $data);
  218. return $data;
  219. }
  220. public function after()
  221. {
  222. $data = Dever::db('rule_after', 'upload')->select(['rule_id' => $this->config['id']]);
  223. if ($data) {
  224. foreach ($data as $k => $v) {
  225. $table = '';
  226. if ($v['type'] == 1) {
  227. $table = 'thumb';
  228. } elseif ($v['type'] == 2) {
  229. $table = 'crop';
  230. } elseif ($v['type'] == 3) {
  231. $table = 'water_pic';
  232. } elseif ($v['type'] == 4) {
  233. $table = 'water_txt';
  234. }
  235. if ($table && $v['type_id']) {
  236. $data[$k]['table'] = $table;
  237. $data[$k]['param'] = Dever::db($table, 'image')->find($v['type_id']);
  238. }
  239. }
  240. }
  241. return $data;
  242. }
  243. public function check($ext, $size, $info, $source = '')
  244. {
  245. $result = $this->checkExt($ext);
  246. if (is_string($result)) {
  247. return $result;
  248. }
  249. if ($size) {
  250. $result = $this->checkSize($size);
  251. if (is_string($result)) {
  252. return $result;
  253. }
  254. if ($this->type == 1 && $info) {
  255. if (is_string($info)) {
  256. $info = $info($source);
  257. }
  258. if ($info) {
  259. $result = $this->checkLimit($info);
  260. if (is_string($result)) {
  261. return $result;
  262. }
  263. }
  264. }
  265. }
  266. return true;
  267. }
  268. protected function checkExt($ext)
  269. {
  270. $this->type = 0;
  271. if (!$ext) {
  272. return '文件格式错误';
  273. }
  274. $state = false;
  275. $type = explode(',', $this->config['type']);
  276. foreach ($type as $v) {
  277. if (isset($this->ext[$v]) && strstr($this->ext[$v], $ext)) {
  278. $this->type = $v;
  279. $state = true;
  280. break;
  281. }
  282. }
  283. if (!$state) {
  284. return '文件格式不符合要求';
  285. }
  286. $this->config['ext'] = $ext;
  287. return true;
  288. }
  289. protected function checkSize($size)
  290. {
  291. $set = $this->config['size'];
  292. if (!$set) {
  293. $set = 2;
  294. }
  295. $limit = $set * 1048576;
  296. if ($size > $limit) {
  297. return '文件不能超过'.$set.'MB';
  298. }
  299. $this->config['size'] = $size;
  300. }
  301. protected function checkLimit($info)
  302. {
  303. if (isset($this->config['limit']) && $this->config['limit'] == 2 && $info[0] < $info[1]) {
  304. return '文件高度不能超过文件宽度';
  305. } elseif (isset($this->config['limit']) && $this->config['limit'] == 3 && $info[0] > $info[1]) {
  306. return '文件宽度不能超过文件高度';
  307. } elseif ($this->config['min_width'] > 0 && $this->config['min_width'] > $info[0]) {
  308. return '文件宽度不能小于' . $this->config['min_width'] . 'px';
  309. } elseif ($this->config['min_height'] > 0 && $this->config['min_height'] > $info[1]) {
  310. return '文件高度不能小于' . $this->config['min_height'] . 'px';
  311. }
  312. $this->config['width'] = $info[0];
  313. $this->config['height'] = $info[1];
  314. }
  315. protected function getDest(&$name, $ext, $uid)
  316. {
  317. if ($uid) {
  318. $id = abs(intval($uid));
  319. $id = sprintf("%09d", $id);
  320. $dest = substr($id, 0, 3) . DIRECTORY_SEPARATOR . substr($id, 3, 2) . DIRECTORY_SEPARATOR . substr($id, 5, 2) . DIRECTORY_SEPARATOR . $uid . '.' . $ext;
  321. $name = $uid;
  322. } else {
  323. if (!strpos($name, '_cr_')) {
  324. $name = md5($name);
  325. }
  326. $path = array_slice(str_split($name, 2), 0, 3);
  327. $dest = implode(DIRECTORY_SEPARATOR, $path) . DIRECTORY_SEPARATOR . $name . '.' . $ext;
  328. }
  329. return $dest;
  330. }
  331. protected function getExtByMine($mine, $flip = false, $result = false)
  332. {
  333. if (!$mine) {
  334. return '';
  335. }
  336. $config = [
  337. 'application/envoy' => 'evy',
  338. 'application/fractals' => 'fif',
  339. 'application/futuresplash' => 'spl',
  340. 'application/hta' => 'hta',
  341. 'application/internet-property-stream' => 'acx',
  342. 'application/mac-binhex40' => 'hqx',
  343. 'application/msword' => 'doc',
  344. 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => 'xlsx',
  345. 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => 'docx',
  346. 'video/x-m4v' => 'mp4',
  347. 'video/mp4' => 'mp4',
  348. 'application/octet-stream' => 'exe',
  349. 'application/oda' => 'oda',
  350. 'application/olescript' => 'axs',
  351. 'application/pdf' => 'pdf',
  352. 'application/pics-rules' => 'prf',
  353. 'application/pkcs10' => 'p10',
  354. 'application/pkix-crl' => 'crl',
  355. 'application/postscript' => 'ai',
  356. 'application/postscript' => 'eps',
  357. 'application/postscript' => 'ps',
  358. 'application/rtf' => 'rtf',
  359. 'application/set-payment-initiation' => 'setpay',
  360. 'application/set-registration-initiation' => 'setreg',
  361. 'application/vnd.ms-excel' => 'xls',
  362. 'application/vnd.ms-outlook' => 'msg',
  363. 'application/vnd.ms-pkicertstore' => 'sst',
  364. 'application/vnd.ms-pkiseccat' => 'cat',
  365. 'application/vnd.ms-pkistl' => 'stl',
  366. 'application/vnd.ms-powerpoint' => 'ppt',
  367. 'application/vnd.ms-project' => 'mpp',
  368. 'application/vnd.ms-works' => 'wps',
  369. 'application/winhlp' => 'hlp',
  370. 'application/x-bcpio' => 'bcpio',
  371. 'application/x-cdf' => 'cdf',
  372. 'application/x-compress' => 'z',
  373. 'application/x-compressed' => 'tgz',
  374. 'application/x-cpio' => 'cpio',
  375. 'application/x-csh' => 'csh',
  376. 'application/x-director' => 'dir',
  377. 'application/x-dvi' => 'dvi',
  378. 'application/x-gtar' => 'gtar',
  379. 'application/x-gzip' => 'gz',
  380. 'application/x-hdf' => 'hdf',
  381. 'application/x-internet-signup' => 'isp',
  382. 'application/x-iphone' => 'iii',
  383. 'application/x-javascript' => 'js',
  384. 'application/x-latex' => 'latex',
  385. 'application/x-msaccess' => 'mdb',
  386. 'application/x-mscardfile' => 'crd',
  387. 'application/x-msclip' => 'clp',
  388. 'application/x-msdownload' => 'dll',
  389. 'application/x-msmediaview' => 'mvb',
  390. 'application/x-msmetafile' => 'wmf',
  391. 'application/x-msmoney' => 'mny',
  392. 'application/x-mspublisher' => 'pub',
  393. 'application/x-msschedule' => 'scd',
  394. 'application/x-msterminal' => 'trm',
  395. 'application/x-mswrite' => 'wri',
  396. 'application/x-netcdf' => 'cdf',
  397. 'application/x-netcdf' => 'nc',
  398. 'application/x-perfmon' => 'pma',
  399. 'application/x-pkcs12' => 'p12',
  400. 'application/x-pkcs12' => 'pfx',
  401. 'application/x-pkcs7-certificates' => 'p7b',
  402. 'application/x-pkcs7-certreqresp' => 'p7r',
  403. 'application/x-pkcs7-mime' => 'p7c',
  404. 'application/x-pkcs7-signature' => 'p7s',
  405. 'application/x-sh' => 'sh',
  406. 'application/x-shar' => 'shar',
  407. 'application/x-shockwave-flash' => 'swf',
  408. 'application/x-stuffit' => 'sit',
  409. 'application/x-sv4cpio' => 'sv4cpio',
  410. 'application/x-sv4crc' => 'sv4crc',
  411. 'application/x-tar' => 'tar',
  412. 'application/x-tcl' => 'tcl',
  413. 'application/x-tex' => 'tex',
  414. 'application/x-texinfo' => 'texi',
  415. 'application/x-texinfo' => 'texinfo',
  416. 'application/x-troff' => 'roff',
  417. 'application/x-troff' => 't',
  418. 'application/x-troff' => 'tr',
  419. 'application/x-troff-man' => 'man',
  420. 'application/x-troff-me' => 'me',
  421. 'application/x-troff-ms' => 'ms',
  422. 'application/x-ustar' => 'ustar',
  423. 'application/x-wais-source' => 'src',
  424. 'application/x-x509-ca-cert' => 'cer',
  425. 'application/ynd.ms-pkipko' => 'pko',
  426. 'application/zip' => 'zip',
  427. 'audio/basic' => 'au',
  428. 'audio/basic' => 'snd',
  429. 'audio/mid' => 'mid',
  430. 'audio/mid' => 'rmi',
  431. 'audio/mpeg' => 'mp3',
  432. 'audio/mp3' => 'mp3',
  433. 'audio/x-aiff' => 'aif',
  434. 'audio/x-aiff' => 'aifc',
  435. 'audio/x-aiff' => 'aiff',
  436. 'audio/x-mpegurl' => 'm3u',
  437. 'audio/x-pn-realaudio' => 'ram',
  438. 'audio/x-wav' => 'wav',
  439. 'image/png' => 'png',
  440. 'image/bmp' => 'bmp',
  441. 'image/cis-cod' => 'cod',
  442. 'image/gif' => 'gif',
  443. 'image/ief' => 'ief',
  444. 'image/jpeg' => 'jpg',
  445. 'image/pipeg' => 'jfif',
  446. 'image/svg+xml' => 'svg',
  447. 'image/tiff' => 'tif',
  448. 'image/tiff' => 'tiff',
  449. 'image/x-cmu-raster' => 'ras',
  450. 'image/x-cmx' => 'cmx',
  451. 'image/x-icon' => 'ico',
  452. 'image/x-portable-anymap' => 'pnm',
  453. 'image/x-portable-bitmap' => 'pbm',
  454. 'image/x-portable-graymap' => 'pgm',
  455. 'image/x-portable-pixmap' => 'ppm',
  456. 'image/x-rgb' => 'rgb',
  457. 'image/x-xbitmap' => 'xbm',
  458. 'image/x-xpixmap' => 'xpm',
  459. 'image/x-xwindowdump' => 'xwd',
  460. 'message/rfc822' => 'mht',
  461. 'message/rfc822' => 'mhtml',
  462. 'message/rfc822' => 'nws',
  463. 'text/css' => 'css',
  464. 'text/h323' => '323',
  465. 'text/html' => 'html',
  466. 'text/iuls' => 'uls',
  467. 'text/plain' => 'txt',
  468. 'text/richtext' => 'rtx',
  469. 'text/scriptlet' => 'sct',
  470. 'text/tab-separated-values' => 'tsv',
  471. 'text/webviewhtml' => 'htt',
  472. 'text/x-component' => 'htc',
  473. 'text/x-setext' => 'etx',
  474. 'text/x-vcard' => 'vcf',
  475. 'video/mpeg' => 'mpeg',
  476. 'video/quicktime' => 'mov',
  477. 'video/x-ms-asf' => 'asx',
  478. 'video/x-msvideo' => 'avi',
  479. 'video/x-sgi-movie' => 'movie',
  480. 'x-world/x-vrml' => 'flr',
  481. 'application/x-rar' => 'rar',
  482. 'application/vnd.android.package-archive' => 'apk',
  483. 'audio/webm' => 'webm',
  484. 'video/webm' => 'webm',
  485. 'audio/x-m4a' => 'm4a',
  486. 'image/webp' => 'webp',
  487. ];
  488. if ($flip) {
  489. $config = array_flip($config);
  490. }
  491. $mine = trim($mine);
  492. if (isset($config[$mine])) {
  493. return $config[$mine];
  494. } else {
  495. return false;
  496. }
  497. }
  498. protected function getExtByByte($file)
  499. {
  500. $file = fopen($file, "rb");
  501. $bin = fread($file, 2);
  502. fclose($file);
  503. $strInfo = @unpack("c2chars",$bin);
  504. $typeCode = intval($strInfo['chars1'].$strInfo['chars2']);
  505. $fileType = '';
  506. switch ($typeCode) {
  507. case 7790:
  508. $fileType = 'exe';
  509. break;
  510. case 7784:
  511. $fileType = 'midi';
  512. break;
  513. case 8297:
  514. $fileType = 'rar';
  515. break;
  516. case 255216:
  517. $fileType = 'jpg';
  518. break;
  519. case 7173:
  520. $fileType = 'gif';
  521. break;
  522. case 13780:
  523. $fileType = 'png';
  524. break;
  525. case 6677:
  526. $fileType = 'bmp';
  527. break;
  528. case 6787:
  529. $fileType = 'swf';
  530. break;
  531. case 6063;
  532. $fileType = 'php|xml';
  533. break;
  534. case 6033:
  535. $fileType = 'html|htm|shtml';
  536. break;
  537. case 8075:
  538. $fileType = 'zip';
  539. break;
  540. case 6782:
  541. case 1310:
  542. $fileType = 'txt';
  543. break;
  544. case 4742:
  545. $fileType = 'js';
  546. break;
  547. case 8273:
  548. $fileType = 'wav';
  549. break;
  550. case 7368:
  551. $fileType = 'mp3';
  552. break;
  553. case 3780:
  554. $fileType = 'pdf';
  555. break;
  556. case 4545:
  557. $fileType = 'pem';
  558. break;
  559. case 7597:
  560. $fileType = 'fbx';
  561. break;
  562. default:
  563. $fileType = 'unknown'.$typeCode;
  564. break;
  565. }
  566. if ($strInfo['chars1'] == '-1' && $strInfo['chars2'] == '-40') {
  567. return 'jpg';
  568. }
  569. if ($strInfo['chars1'] == '-119' && $strInfo['chars2'] == '80') {
  570. return 'png';
  571. }
  572. if ($strInfo['chars1'] == '-48' && $strInfo['chars2'] == '-49') {
  573. return 'msi';
  574. }
  575. return $fileType;
  576. }
  577. }