// +---------------------------------------------------------------------- namespace ywxapp\services; use think\facade\Filesystem; use think\facade\Request; /** * UEditorService 类 * * @author ywxapp */ class UEditorService { public function __construct() { header("Access-Control-Allow-Origin: *"); header("Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, Authorization"); header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS"); if (request()->method() == 'OPTIONS') { exit(); } } /** * UEditor 后端接口 */ public function index() { $action = Request::param('action'); switch ($action) { case 'config': $result = $this->getConfig(); break; case 'uploadimage': $result = $this->uploadImage(); break; case 'uploadscrawl': $result = $this->uploadScrawl(); break; case 'uploadvideo': $result = $this->uploadVideo(); break; case 'uploadfile': $result = $this->uploadFile(); break; case 'listimage': $result = $this->listImage(); break; case 'listfile': $result = $this->listFile(); break; case 'catchimage': $result = $this->catchImage(); break; default: $result = [ 'state' => '请求地址出错', ]; break; } return json($result); } /** * 获取配置 */ private function getConfig() { return [ /* 上传图片配置项 */ "imageActionName" => "uploadimage", "imageFieldName" => "upfile", "imageMaxSize" => 2048000, "imageAllowFiles" => [".png", ".jpg", ".jpeg", ".gif", ".bmp"], "imageCompressEnable" => true, "imageCompressBorder" => 1600, "imageInsertAlign" => "none", "imageUrlPrefix" => "", /* 涂鸦图片上传配置项 */ "scrawlActionName" => "uploadscrawl", "scrawlFieldName" => "upfile", "scrawlMaxSize" => 2048000, "scrawlUrlPrefix" => "", "scrawlInsertAlign" => "none", /* 截图工具上传 */ "snapscreenActionName" => "uploadimage", "snapscreenUrlPrefix" => "", "snapscreenInsertAlign" => "none", /* 抓取远程图片配置 */ "catcherLocalDomain" => ["127.0.0.1", "localhost"], "catcherActionName" => "catchimage", "catcherFieldName" => "source", "catcherUrlPrefix" => "", "catcherMaxSize" => 2048000, "catcherAllowFiles" => [".png", ".jpg", ".jpeg", ".gif", ".bmp"], /* 上传视频配置 */ "videoActionName" => "uploadvideo", "videoFieldName" => "upfile", "videoMaxSize" => 102400000, "videoUrlPrefix" => "", "videoAllowFiles" => [ ".flv", ".swf", ".mkv", ".avi", ".rm", ".rmvb", ".mpeg", ".mpg", ".ogg", ".ogv", ".mov", ".wmv", ".mp4", ".webm", ".mp3", ".wav", ".mid", ], /* 上传文件配置 */ "fileActionName" => "uploadfile", "fileFieldName" => "upfile", "fileMaxSize" => 51200000, "fileUrlPrefix" => "", "fileAllowFiles" => [ ".png", ".jpg", ".jpeg", ".gif", ".bmp", ".flv", ".swf", ".mkv", ".avi", ".rm", ".rmvb", ".mpeg", ".mpg", ".ogg", ".ogv", ".mov", ".wmv", ".mp4", ".webm", ".mp3", ".wav", ".mid", ".rar", ".zip", ".tar", ".gz", ".7z", ".bz2", ".cab", ".iso", ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".pdf", ".txt", ".md", ".xml", ], /* 列出指定目录下的图片 */ "imageManagerActionName" => "listimage", "imageManagerListPath" => "/upload/image/", "imageManagerListSize" => 20, "imageManagerUrlPrefix" => "", "imageManagerInsertAlign" => "none", "imageManagerAllowFiles" => [".png", ".jpg", ".jpeg", ".gif", ".bmp"], /* 列出指定目录下的文件 */ "fileManagerActionName" => "listfile", "fileManagerListPath" => "/upload/file/", "fileManagerListSize" => 20, "fileManagerUrlPrefix" => "", "fileManagerAllowFiles" => [ ".png", ".jpg", ".jpeg", ".gif", ".bmp", ".flv", ".swf", ".mkv", ".avi", ".rm", ".rmvb", ".mpeg", ".mpg", ".ogg", ".ogv", ".mov", ".wmv", ".mp4", ".webm", ".mp3", ".wav", ".mid", ".rar", ".zip", ".tar", ".gz", ".7z", ".bz2", ".cab", ".iso", ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".pdf", ".txt", ".md", ".xml", ], /* 配置项 */ "imageUrlPrefix" => Request::domain() , /* 图片访问路径前缀 */ "scrawlUrlPrefix" => Request::domain() , /* 涂鸦访问路径前缀 */ "snapscreenUrlPrefix" => Request::domain() , /* 截图访问路径前缀 */ "catcherUrlPrefix" => Request::domain() , /* 图片访问路径前缀 */ "videoUrlPrefix" => Request::domain() , /* 视频访问路径前缀 */ "fileUrlPrefix" => Request::domain() , /* 文件访问路径前缀 */ "imagePathFormat" => "/uploads/images/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */ "scrawlPathFormat" => "/uploads/scrawl/{yyyy}{mm}{dd}/{time}{rand:6}", /* 涂鸦上传保存路径 */ "snapscreenPathFormat" => "/uploads/images/{yyyy}{mm}{dd}/{time}{rand:6}", /* 截图上传保存路径 */ "catcherPathFormat" => "/uploads/images/{yyyy}{mm}{dd}/{time}{rand:6}", /* 抓取图片上传保存路径 */ "videoPathFormat" => "/uploads/videos/{yyyy}{mm}{dd}/{time}{rand:6}", /* 视频上传保存路径 */ "filePathFormat" => "/uploads/files/{yyyy}{mm}{dd}/{time}{rand:6}", /* 文件上传保存路径 */ ]; } /** * 上传图片 */ private function uploadImage() { return $this->upload('image'); } /** * 上传涂鸦 */ private function uploadScrawl() { $file = Request::post('upfile'); if (empty($file)) { return ['state' => '上传文件为空']; } // 生成文件名 $filename = date('Ymd') . '/' . uniqid() . '.png'; // 保存路径 $savePath = 'uploads/scrawl/' . $filename; $fullPath = public_path() . $savePath; // 确保目录存在 $dir = dirname($fullPath); if (! is_dir($dir)) { mkdir($dir, 0755, true); } // 解码并保存base64图片 $img = base64_decode(preg_replace('#^data:image/\w+;base64,#i', '', $file)); file_put_contents($fullPath, $img); return [ "state" => "SUCCESS", "url" => Request::domain() . '/' . $savePath, "title" => $filename, "original" => "scrawl.png", "type" => ".png", "size" => strlen($img), ]; } /** * 上传视频 */ private function uploadVideo() { return $this->upload('video'); } /** * 上传文件 */ private function uploadFile() { return $this->upload('file'); } /** * 通用上传方法 */ private function upload($type) { $file = Request::file('upfile'); if (empty($file)) { return ['state' => '上传文件为空']; } // 验证文件 $config = $this->getConfig(); $maxSize = $config[$type . 'MaxSize']; $allowFiles = $config[$type . 'AllowFiles']; try { $validate = [ 'size' => $maxSize, 'ext' => implode(',', array_map(function ($ext) { return ltrim($ext, '.'); }, $allowFiles)), ]; $savename = Filesystem::disk('public')->putFile('uploads/' . $type, $file); return [ "state" => "SUCCESS", "url" => '/storage/' . $savename, "title" => basename($savename), "original" => $file->getOriginalName(), "type" => '.' . $file->extension(), "size" => $file->getSize(), ]; } catch (\Exception $e) { return ['state' => $e->getMessage()]; } } /** * 列出图片 */ private function listImage() { return $this->listFiles('image'); } /** * 列出文件 */ private function listFile() { return $this->listFiles('file'); } /** * 列出文件通用方法 */ private function listFiles($type) { $config = $this->getConfig(); $allowFiles = $config[$type . 'ManagerAllowFiles']; $listSize = $config[$type . 'ManagerListSize']; $path = $config[$type . 'ManagerListPath']; $start = Request::param('start', 0); $size = Request::param('size', $listSize); $files = $this->getFiles(public_path() . $path, $allowFiles); if (! count($files)) { return [ "state" => "no match file", "list" => [], "start" => $start, "total" => 0, ]; } // 排序 usort($files, function ($a, $b) { return strcmp($a['mtime'], $b['mtime']); }); // 分页 $files = array_slice($files, $start, $size); return [ "state" => "SUCCESS", "list" => $files, "start" => $start, "total" => count($files), ]; } /** * 获取目录下的文件 */ private function getFiles($path, $allowFiles, &$files = []) { if (! is_dir($path)) { return []; } $handle = opendir($path); while (false !== ($file = readdir($handle))) { if ($file != '.' && $file != '..') { $path2 = $path . '/' . $file; if (is_dir($path2)) { $this->getFiles($path2, $allowFiles, $files); } else { if (preg_match("/\.(" . str_replace('.', '', implode('|', $allowFiles)) . ")$/i", $file)) { $files[] = [ 'url' => Request::domain() . str_replace(public_path(), '', $path2), 'mtime' => filemtime($path2), ]; } } } } return $files; } /** * 抓取远程图片 */ private function catchImage() { $sources = Request::param('source', []); if (empty($sources)) { return ['state' => '参数错误:没有指定抓取源']; } $list = []; foreach ($sources as $imgUrl) { // 验证域名 $config = $this->getConfig(); $domain = parse_url($imgUrl, PHP_URL_HOST); if (! in_array($domain, $config['catcherLocalDomain'])) { $list[] = [ 'state' => '抓取远程图片失败,域名不允许', 'url' => $imgUrl, 'source' => $imgUrl, ]; continue; } // 获取图片 $imgContent = file_get_contents($imgUrl); if ($imgContent === false) { $list[] = [ 'state' => '抓取远程图片失败', 'url' => $imgUrl, 'source' => $imgUrl, ]; continue; } // 验证图片 $imgInfo = getimagesizefromstring($imgContent); if (! $imgInfo) { $list[] = [ 'state' => '不是有效的图片文件', 'url' => $imgUrl, 'source' => $imgUrl, ]; continue; } // 生成文件名 $ext = image_type_to_extension($imgInfo[2]); $filename = date('Ymd') . '/' . uniqid() . $ext; $savePath = 'uploads/images/' . $filename; $fullPath = public_path() . $savePath; // 确保目录存在 $dir = dirname($fullPath); if (! is_dir($dir)) { mkdir($dir, 0755, true); } // 保存文件 file_put_contents($fullPath, $imgContent); $list[] = [ 'state' => 'SUCCESS', 'url' => Request::domain() . '/' . $savePath, 'source' => $imgUrl, ]; } return [ 'state' => count($list) ? 'SUCCESS' : 'ERROR', 'list' => $list, ]; } }