UeditorController.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | ThinkCMF [ WE CAN DO IT MORE SIMPLE ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2013-2017 http://www.thinkcmf.com All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8. // +----------------------------------------------------------------------
  9. // | Author: kane <chengjin005@163.com>
  10. // +----------------------------------------------------------------------
  11. namespace admin\user\controller;
  12. use cmf\controller\HomeBaseController;
  13. use cmf\lib\Upload;
  14. /**
  15. * 百度编辑器文件上传处理控制器
  16. * Class Ueditor
  17. *
  18. * @package app\asset\controller
  19. */
  20. class UeditorController extends HomeBaseController {
  21. private $stateMap
  22. = [ //上传状态映射表,国际化用户需考虑此处数据的国际化
  23. "SUCCESS", //上传成功标记,在UEditor中内不可改变,否则flash判断会出错
  24. "文件大小超出 upload_max_filesize 限制",
  25. "文件大小超出 MAX_FILE_SIZE 限制",
  26. "文件未被完整上传",
  27. "没有文件被上传",
  28. "上传文件为空",
  29. "ERROR_TMP_FILE" => "临时文件错误",
  30. "ERROR_TMP_FILE_NOT_FOUND" => "找不到临时文件",
  31. "ERROR_SIZE_EXCEED" => "文件大小超出网站限制",
  32. "ERROR_TYPE_NOT_ALLOWED" => "文件类型不允许",
  33. "ERROR_CREATE_DIR" => "目录创建失败",
  34. "ERROR_DIR_NOT_WRITEABLE" => "目录没有写权限",
  35. "ERROR_FILE_MOVE" => "文件保存时出错",
  36. "ERROR_FILE_NOT_FOUND" => "找不到上传文件",
  37. "ERROR_WRITE_CONTENT" => "写入文件内容错误",
  38. "ERROR_UNKNOWN" => "未知错误",
  39. "ERROR_DEAD_LINK" => "链接不可用",
  40. "ERROR_HTTP_LINK" => "链接不是http链接",
  41. "ERROR_HTTP_CONTENTTYPE" => "链接contentType不正确"
  42. ];
  43. /**
  44. * 初始化
  45. */
  46. function _initialize() {
  47. $adminId = cmf_get_current_admin_id();
  48. $userId = cmf_get_current_user_id();
  49. if (empty($adminId) && empty($userId)) {
  50. exit("非法上传!");
  51. }
  52. }
  53. /**
  54. * 处理上传处理
  55. */
  56. function upload() {
  57. error_reporting(E_ERROR);
  58. header("Content-Type: text/html; charset=utf-8");
  59. $action = $this->request->param('action');
  60. switch ($action) {
  61. case 'config':
  62. $result = $this->ueditorConfig();
  63. break;
  64. /* 上传图片 */
  65. case 'uploadimage':
  66. $result = $this->ueditorUpload("image");
  67. break;
  68. /* 上传涂鸦 */
  69. case 'uploadscrawl':
  70. $result = $this->ueditorUpload("image");
  71. break;
  72. /* 上传视频 */
  73. case 'uploadvideo':
  74. $result = $this->ueditorUpload("video");
  75. break;
  76. /* 上传文件 */
  77. case 'uploadfile':
  78. $result = $this->ueditorUpload("file");
  79. break;
  80. /* 列出图片 */
  81. case 'listimage':
  82. $result = "";
  83. break;
  84. /* 列出文件 */
  85. case 'listfile':
  86. $result = "";
  87. break;
  88. /* 抓取远程文件 */
  89. case 'catchimage':
  90. $result = $this->_get_remote_image();
  91. break;
  92. default:
  93. $result = json_encode(['state' => '请求地址出错']);
  94. break;
  95. }
  96. /* 输出结果 */
  97. if (isset($_GET["callback"]) && false) {//TODO 跨域上传
  98. if (preg_match("/^[\w_]+$/", $_GET["callback"])) {
  99. echo htmlspecialchars($_GET["callback"]).'('.$result.')';
  100. } else {
  101. echo json_encode(
  102. [
  103. 'state' => 'callback参数不合法'
  104. ]
  105. );
  106. }
  107. } else {
  108. exit($result);
  109. }
  110. }
  111. /**
  112. * 获取远程图片
  113. */
  114. private function _get_remote_image() {
  115. $source = $this->request->param('source');
  116. $item = [
  117. "state" => "",
  118. "url" => "",
  119. "size" => "",
  120. "title" => "",
  121. "original" => "",
  122. "source" => ""
  123. ];
  124. $date = date("Ymd");
  125. $uploadSetting = cmf_get_upload_setting();
  126. $uploadMaxFileSize = $uploadSetting["image"]['upload_max_filesize'];
  127. $uploadMaxFileSize = empty($uploadMaxFileSize) ? 2048 : $uploadMaxFileSize;//默认2M
  128. $allowedExts = explode(',', $uploadSetting["image"]["extensions"]);
  129. $strSavePath = ROOT_PATH.'public'.DS."ueditor".DS.$date.DS;
  130. //远程抓取图片配置
  131. $config = [
  132. "savePath" => $strSavePath, //保存路径
  133. "allowFiles" => $allowedExts,// [".gif", ".png", ".jpg", ".jpeg", ".bmp"], //文件允许格式
  134. "maxSize" => $uploadMaxFileSize //文件大小限制,单位KB
  135. ];
  136. $storage_setting = cmf_get_cmf_settings('storage');
  137. $qiniu_domain = $storage_setting['Qiniu']['domain'];
  138. $no_need_domains = [$qiniu_domain];
  139. $list = [];
  140. foreach ($source as $imgUrl) {
  141. $host = str_replace(['http://', 'https://'], '', $imgUrl);
  142. $host = explode('/', $host);
  143. $host = $host[0];
  144. if (in_array($host, $no_need_domains)) {
  145. continue;
  146. }
  147. $return_img = $item;
  148. $return_img['source'] = $imgUrl;
  149. $imgUrl = htmlspecialchars($imgUrl);
  150. $imgUrl = str_replace("&amp;", "&", $imgUrl);
  151. //http开头验证
  152. if (strpos($imgUrl, "http") !== 0) {
  153. $return_img['state'] = $this->stateMap['ERROR_HTTP_LINK'];
  154. array_push($list, $return_img);
  155. continue;
  156. }
  157. //获取请求头
  158. // is_sae()
  159. if (!cmf_is_sae()) {//SAE下无效
  160. $heads = get_headers($imgUrl);
  161. //死链检测
  162. if (!(stristr($heads[0], "200") && stristr($heads[0], "OK"))) {
  163. $return_img['state'] = $this->stateMap['ERROR_DEAD_LINK'];
  164. array_push($list, $return_img);
  165. continue;
  166. }
  167. }
  168. //格式验证(扩展名验证和Content-Type验证)
  169. $fileType = strtolower(strrchr($imgUrl, '.'));
  170. if (!in_array($fileType, $config['allowFiles']) || stristr($heads['Content-Type'], "image")) {
  171. $return_img['state'] = $this->stateMap['ERROR_HTTP_CONTENTTYPE'];
  172. array_push($list, $return_img);
  173. continue;
  174. }
  175. //打开输出缓冲区并获取远程图片
  176. ob_start();
  177. $context = stream_context_create(
  178. [
  179. 'http' => [
  180. 'follow_location' => false // don't follow redirects
  181. ]
  182. ]
  183. );
  184. //请确保php.ini中的fopen wrappers已经激活
  185. readfile($imgUrl, false, $context);
  186. $img = ob_get_contents();
  187. ob_end_clean();
  188. //大小验证
  189. $uriSize = strlen($img); //得到图片大小
  190. $allowSize = 1024 * $config['maxSize'];
  191. if ($uriSize > $allowSize) {
  192. $return_img['state'] = $this->stateMap['ERROR_SIZE_EXCEED'];
  193. array_push($list, $return_img);
  194. continue;
  195. }
  196. $file = uniqid().strrchr($imgUrl, '.');
  197. $savePath = $config['savePath'];
  198. $tmpName = $savePath.$file;
  199. //创建保存位置
  200. if (!file_exists($savePath)) {
  201. mkdir("$savePath", 0777, true);
  202. }
  203. $file_write_result = cmf_file_write($tmpName, $img);
  204. if ($file_write_result) {
  205. if (config('FILE_UPLOAD_TYPE') == 'Qiniu') {
  206. //todo qiniu code
  207. }
  208. if (config('FILE_UPLOAD_TYPE') == 'Local') {
  209. $file = $strSavePath.$file;
  210. $return_img['state'] = 'SUCCESS';
  211. $return_img['url'] = $file;
  212. array_push($list, $return_img);
  213. }
  214. } else {
  215. $return_img['state'] = $this->stateMap['ERROR_WRITE_CONTENT'];
  216. array_push($list, $return_img);
  217. }
  218. }
  219. return json_encode(
  220. [
  221. 'state' => count($list) ? 'SUCCESS' : 'ERROR',
  222. 'list' => $list
  223. ]
  224. );
  225. }
  226. /**
  227. * 文件上传
  228. *
  229. * @param string $fileType 文件类型
  230. *
  231. * @return string
  232. */
  233. private function ueditorUpload($fileType = 'image') {
  234. $uploader = new Upload();
  235. $uploader->setFileType($fileType);
  236. $uploader->setFormName('upfile');
  237. $result = $uploader->upload();
  238. if ($result === false) {
  239. return json_encode(
  240. [
  241. 'state' => $uploader->getError()
  242. ]
  243. );
  244. } else {
  245. return json_encode(
  246. [
  247. 'state' => 'SUCCESS',
  248. 'url' => $result['url'],
  249. 'title' => $result['name'],
  250. 'original' => $result['name']
  251. ]
  252. );
  253. }
  254. }
  255. /**
  256. * 获取百度编辑器配置
  257. */
  258. private function ueditorConfig() {
  259. $config_text = preg_replace(
  260. "/\/\*[\s\S]+?\*\//", "", file_get_contents(STATICSITE."/admin/js/ueditor/config.json")
  261. );
  262. $config = json_decode($config_text, true);
  263. $upload_setting = cmf_get_upload_setting();
  264. $config['imageMaxSize'] = $upload_setting['file_types']['image']['upload_max_filesize'] * 1024;
  265. $config['imageAllowFiles'] = array_map(
  266. [$this, 'ueditorExtension'], explode(",", $upload_setting['file_types']['image']['extensions'])
  267. );
  268. $config['scrawlMaxSize'] = $upload_setting['file_types']['image']['upload_max_filesize'] * 1024;
  269. //
  270. $config['catcherMaxSize'] = $upload_setting['file_types']['image']['upload_max_filesize'] * 1024;
  271. $config['catcherAllowFiles'] = array_map(
  272. [$this, 'ueditorExtension'], explode(",", $upload_setting['file_types']['image']['extensions'])
  273. );
  274. $config['videoMaxSize'] = $upload_setting['file_types']['video']['upload_max_filesize'] * 1024;
  275. $config['videoAllowFiles'] = array_map(
  276. [$this, 'ueditorExtension'], explode(",", $upload_setting['file_types']['video']['extensions'])
  277. );
  278. $config['fileMaxSize'] = $upload_setting['file_types']['file']['upload_max_filesize'] * 1024;
  279. $config['fileAllowFiles'] = array_map(
  280. [$this, 'ueditorExtension'], explode(",", $upload_setting['file_types']['file']['extensions'])
  281. );
  282. return json_encode($config);
  283. }
  284. /**
  285. * 格式化后缀
  286. *
  287. * @param $str
  288. *
  289. * @return string
  290. */
  291. private function ueditorExtension($str) {
  292. return ".".trim($str, '.');
  293. }
  294. /**
  295. * @function imageManager
  296. */
  297. public function imageManager() {
  298. header("Content-Type: text/html; charset=utf-8");
  299. //需要遍历的目录列表,最好使用缩略图地址,否则当网速慢时可能会造成严重的延时
  300. $paths = [C("TMPL_PARSE_STRING.__UPLOAD__"), 'upload/'];
  301. $files = [];
  302. foreach ($paths as $path) {
  303. $tmp = $this->getfiles($path);
  304. if ($tmp) {
  305. $files = array_merge($files, $tmp);
  306. }
  307. }
  308. if (!count($files)) {
  309. return;
  310. }
  311. rsort($files, SORT_STRING);
  312. $str = "";
  313. foreach ($files as $file) {
  314. $str .= ROOT_PATH.'/'.$file."ue_separate_ue";
  315. }
  316. echo $str;
  317. }
  318. /**
  319. * 遍历获取目录下的指定类型的文件
  320. *
  321. * @param $path
  322. * @param array $files
  323. *
  324. * @return array
  325. */
  326. private function getfiles($path, $allowFiles, &$files = []) {
  327. if (!is_dir($path)) {
  328. return null;
  329. }
  330. if (substr($path, strlen($path) - 1) != '/') {
  331. $path .= '/';
  332. }
  333. $handle = opendir($path);
  334. while (false !== ($file = readdir($handle))) {
  335. if ($file != '.' && $file != '..') {
  336. $path2 = $path.$file;
  337. if (is_dir($path2)) {
  338. $this->getfiles($path2, $allowFiles, $files);
  339. } else {
  340. if (preg_match("/\.(".$allowFiles.")$/i", $file)) {
  341. $files[] = [
  342. 'url' => substr($path2, strlen($_SERVER['DOCUMENT_ROOT'])),
  343. 'mtime' => filemtime($path2)
  344. ];
  345. }
  346. }
  347. }
  348. }
  349. return $files;
  350. }
  351. }