啥都不说,开整
后端框架 laravel
ueditor.config.js 修改
//改成自己的就好serverUrl: "http://127.0.0.1:8098/api/ueditor/eventHandler"
config目录新增 ueditor.php
return [// 上传图片配置项'imageActionName' => 'uploadimage', // 执行上传图片的action名称'imageFieldName' => 'upfile', // 提交的图片表单名称'imageMaxSize' => 2048000, // 上传大小限制,单位B'imageAllowFiles' => ['.png', '.jpg', '.jpeg', '.gif', '.bmp'], // 上传图片格式显示'imageCompressEnable'=> true, // 是否压缩图片,默认是true'imageCompressBorder'=> 1600, // 图片压缩最长边限制'imageInsertAlign' => 'none', // 插入的图片浮动方式'imageUrlPrefix' => '', // 图片访问路径前缀'imagePathFormat' => '/upload/image/{yyyy}{mm}{dd}/{time}{rand:6}', // 上传保存路径,可以自定义保存路径和文件名格式// {filename} 会替换成原文件名,配置这项需要注意中文乱码问题// {rand=>6} 会替换成随机数,后面的数字是随机数的位数// {time} 会替换成时间戳// {yyyy} 会替换成四位年份// {yy} 会替换成两位年份// {mm} 会替换成两位月份// {dd} 会替换成两位日期// {hh} 会替换成两位小时// {ii} 会替换成两位分钟// {ss} 会替换成两位秒// 非法字符 \ => * ? " < > |// 具请体看线上文档=> fex.baidu.com/ueditor/#use-format_upload_filename// 涂鸦图片上传配置项'scrawlActionName' => 'uploadscrawl', // 执行上传涂鸦的action名称'scrawlFieldName' => 'upfile', // 提交的图片表单名称'scrawlPathFormat' => '/ueditor/php/upload/image/{yyyy}{mm}{dd}/{time}{rand:6}', // 上传保存路径,可以自定义保存路径和文件名格式'scrawlMaxSize' => 2048000, // 上传大小限制,单位B'scrawlUrlPrefix' => '', // 图片访问路径前缀'scrawlInsertAlign'=> 'none',// 截图工具上传'snapscreenActionName' => 'uploadimage', // 执行上传截图的action名称'snapscreenPathFormat' => '/upload/image/{yyyy}{mm}{dd}/{time}{rand:6}', // 上传保存路径,可以自定义保存路径和文件名格式'snapscreenUrlPrefix' => '', // 图片访问路径前缀'snapscreenInsertAlign'=> 'none', // 插入的图片浮动方式// 抓取远程图片配置'catcherLocalDomain'=> ['127.0.0.1', 'localhost', 'img.baidu.com'],'catcherActionName' => 'catchimage', // 执行抓取远程图片的action名称'catcherFieldName' => 'source', // 提交的图片列表表单名称'catcherPathFormat' => '/upload/image/{yyyy}{mm}{dd}/{time}{rand:6}', // 上传保存路径,可以自定义保存路径和文件名格式'catcherUrlPrefix' => '', // 图片访问路径前缀'catcherMaxSize' => 2048000, // 上传大小限制,单位B'catcherAllowFiles' => ['.png', '.jpg', '.jpeg', '.gif', '.bmp'], // 抓取图片格式显示// 上传视频配置'videoActionName'=> 'uploadvideo', // 执行上传视频的action名称'videoFieldName' => 'upfile', // 提交的视频表单名称'videoPathFormat'=> '/upload/video/{yyyy}{mm}{dd}/{time}{rand:6}', // 上传保存路径,可以自定义保存路径和文件名格式'videoUrlPrefix' => '', // 视频访问路径前缀'videoMaxSize' => 102400000, // 上传大小限制,单位B,默认100MB'videoAllowFiles'=> ['.flv', '.swf', '.mkv', '.avi', '.rm', '.rmvb', '.mpeg', '.mpg','.ogg', '.ogv', '.mov', '.wmv', '.mp4', '.webm', '.mp3', '.wav', '.mid', ], // 上传视频格式显示// 上传文件配置'fileActionName'=> 'uploadfile', // controller里,执行上传视频的action名称'fileFieldName' => 'upfile', // 提交的文件表单名称'filePathFormat'=> '/upload/file/{yyyy}{mm}{dd}/{time}{rand:6}', // 上传保存路径,可以自定义保存路径和文件名格式'fileUrlPrefix' => '', // 文件访问路径前缀'fileMaxSize' => 51200000, // 上传大小限制,单位B,默认50MB'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', // 执行图片管理的action名称'imageManagerListPath' => '/upload/image/', // 指定要列出图片的目录'imageManagerListSize' => 20, // 每次列出文件数量'imageManagerUrlPrefix' => '', // 图片访问路径前缀'imageManagerInsertAlign'=> 'none', // 插入的图片浮动方式'imageManagerAllowFiles' => ['.png', '.jpg', '.jpeg', '.gif', '.bmp'], // 列出的文件类型// 列出指定目录下的文件'fileManagerActionName'=> 'listfile', // 执行文件管理的action名称'fileManagerListPath' => '/upload/file/', // 指定要列出文件的目录'fileManagerUrlPrefix' => '', // 文件访问路径前缀'fileManagerListSize' => 20, // 每次列出文件数量'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',], // 列出的文件类型];
后端代码请自行设置允许跨域
namespace App\Http\Controllers;use App\Http\Service\UeditorService;use Illuminate\Http\Request;class UeditorController extends Controller{/*** ueditor 入口程序.** @return false|string*/public function index(Request $request){$action = $request->input('action', 'uploadimage');$ueditor = config('ueditor');switch ($action) {case 'config':$result = json_encode($ueditor);break;case 'uploadimage':$config = ['pathFormat' => $ueditor['imagePathFormat'],'maxSize' => $ueditor['imageMaxSize'],'allowFiles' => $ueditor['imageAllowFiles'],];$fieldName = $ueditor['imageFieldName'];$up = new UeditorService($fieldName, $config, 'upload');$result = json_encode($up->getFileInfo());break;case 'uploadscrawl':$config = ['pathFormat' => $ueditor['scrawlPathFormat'],'maxSize' => $ueditor['scrawlMaxSize'],'allowFiles' => $ueditor['scrawlAllowFiles'],'oriName' => 'scrawl.png',];$fieldName = $ueditor['scrawlFieldName'];$base64 = 'base64';$up = new UeditorService($fieldName, $config, $base64);$result = json_encode($up->getFileInfo());break;case 'uploadvideo':$config = ['pathFormat' => $ueditor['videoPathFormat'],'maxSize' => $ueditor['videoMaxSize'],'allowFiles' => $ueditor['videoAllowFiles'],];$fieldName = $ueditor['videoFieldName'];$up = new UeditorService($fieldName, $config, 'upload');$result = json_encode($up->getFileInfo());break;case 'uploadfile':$config = ['pathFormat' => $ueditor['filePathFormat'],'maxSize' => $ueditor['fileMaxSize'],'allowFiles' => $ueditor['fileAllowFiles'],];$fieldName = $ueditor['fileFieldName'];$up = new UeditorService($fieldName, $config, 'upload');$result = json_encode($up->getFileInfo());break;// 列出图片case 'listimage':$result = json_encode(UeditorService::listFiles($request->start, $request->size, 'imagePathFormat'));break;// 列出文件case 'listfile':$result = json_encode(UeditorService::listFiles($request->start, $request->size, 'filePathFormat'));break;// 抓取远程文件case 'catchimage':// 上传配置$config = ['pathFormat' => $ueditor['catcherPathFormat'],'maxSize' => $ueditor['catcherMaxSize'],'allowFiles' => $ueditor['catcherAllowFiles'],'oriName' => 'remote.png',];$fieldName = $ueditor['catcherFieldName'];$up = new UeditorService($fieldName, $config, 'remote');$result = json_encode($up->getFileInfo());break;default:$result = json_encode(['state'=> '请求地址出错',]);break;}// 输出结果if (isset($_GET['callback'])) {if (preg_match('/^[\\w_]+$/', $_GET['callback'])) {return htmlspecialchars($_GET['callback']) . '(' . $result . ')';}return json_encode(['state'=> 'callback参数不合法',]);}return $result;}}
service
namespace App\Http\Service;use Carbon\Carbon;class UeditorService{private $fileField; //文件域名private $file; //文件上传对象private $base64; //文件上传对象private $config; //配置信息private $oriName; //原始文件名private $fileName; //新文件名private $fullName; //完整文件名,即从当前配置目录开始的URLprivate $filePath; //完整文件名,即从当前配置目录开始的URLprivate $fileSize; //文件大小private $fileType; //文件类型private $stateInfo; //上传状态信息,private $stateMap = [ //上传状态映射表,国际化用户需考虑此处数据的国际化'SUCCESS', //上传成功标记,在UEditor中内不可改变,否则flash判断会出错'文件大小超出 upload_max_filesize 限制','文件大小超出 MAX_FILE_SIZE 限制','文件未被完整上传','没有文件被上传','上传文件为空','ERROR_TMP_FILE' => '临时文件错误','ERROR_TMP_FILE_NOT_FOUND' => '找不到临时文件','ERROR_SIZE_EXCEED' => '文件大小超出网站限制','ERROR_TYPE_NOT_ALLOWED' => '文件类型不允许','ERROR_CREATE_DIR' => '目录创建失败','ERROR_DIR_NOT_WRITEABLE' => '目录没有写权限','ERROR_FILE_MOVE' => '文件保存时出错','ERROR_FILE_NOT_FOUND' => '找不到上传文件','ERROR_WRITE_CONTENT' => '写入文件内容错误','ERROR_UNKNOWN' => '未知错误','ERROR_DEAD_LINK' => '链接不可用','ERROR_HTTP_LINK' => '链接不是http链接','ERROR_HTTP_CONTENTTYPE' => '链接contentType不正确',];/*** @var UploadService*/private $ossService;/*** @var mixed|string*/private $path; //自定义文件存储路径/*** 构造函数.** @param string $fileField 表单名称* @param array $config 配置项* @param string $type 处理文件上传的方式* @param mixed $path*/public function __construct($fileField, $config, $type = 'upload', $path = 'upload/news/'){parent::__construct();$this->ossService = new OssService();$this->path = $path;$this->fileField = $fileField;$this->config = $config;$this->type = $type;if ('remote' == $type) {$this->saveRemote();} elseif ('base64' == $type) {$this->upBase64();} else {$this->upFile();}$this->stateMap['ERROR_TYPE_NOT_ALLOWED'] = mb_convert_encoding($this->stateMap['ERROR_TYPE_NOT_ALLOWED'], 'utf-8', 'auto');}/*** 上传文件的主处理方法.** @return mixed*/private function upFile(){$file = $this->file = \Request::capture()->file($this->fileField);if (!$file) {$this->stateInfo = $this->getStateInfo('ERROR_FILE_NOT_FOUND');return;}if ($this->file->getError()) {$this->stateInfo = $this->getStateInfo($file->getError());return;}if (!file_exists($file->getPathname())) {$this->stateInfo = $this->getStateInfo('ERROR_TMP_FILE_NOT_FOUND');return;}if (!is_uploaded_file($file->getPathname())) {$this->stateInfo = $this->getStateInfo('ERROR_TMPFILE');return;}$this->oriName = $file->getClientOriginalName();$this->fileSize = $file->getSize();$this->fileType = $this->getFileExt();$this->fullName = $this->getFullName();$this->filePath = $this->getFilePath();$this->fileName = $this->getFileName();$dirname = dirname($this->filePath);//检查文件大小是否超出限制if (!$this->checkSize()) {$this->stateInfo = $this->getStateInfo('ERROR_SIZE_EXCEED');return;}//检查是否不允许的文件格式if (!$this->checkType()) {$this->stateInfo = $this->getStateInfo('ERROR_TYPE_NOT_ALLOWED');return;}if ($obj = $this->ossService->put($this->fullName, $file)) {$this->fullName = $obj;$this->stateInfo = $this->stateMap[0];} else {$this->stateInfo = $this->getStateInfo('ERROR_WRITE_CONTENT');}}/*** 处理base64编码的图片上传.** @return mixed*/private function upBase64(){$base64Data = $_POST[$this->fileField];$img = base64_decode($base64Data);$this->oriName = $this->config['oriName'];$this->fileSize = strlen($img);$this->fileType = $this->getFileExt();$this->fullName = $this->getFullName();$this->filePath = $this->getFilePath();$this->fileName = $this->getFileName();$dirname = dirname($this->filePath);//检查文件大小是否超出限制if (!$this->checkSize()) {$this->stateInfo = $this->getStateInfo('ERROR_SIZE_EXCEED');return;}if ($obj = $this->ossService->putObject($this->fullName, $img)) {$this->fullName = $obj;$this->stateInfo = $this->stateMap[0];} else {$this->stateInfo = $this->getStateInfo('ERROR_WRITE_CONTENT');}}/*** 拉取远程图片.** @return mixed*/private function saveRemote(){$imgUrl = htmlspecialchars($this->fileField);$imgUrl = str_replace('&', '&', $imgUrl);//获取带有GET参数的真实图片url路径$pathRes = parse_url($imgUrl);$queryString = $pathRes['query'] ?? '';$imgUrl = str_replace('?' . $queryString, '', $imgUrl);//http开头验证if (0 !== strpos($imgUrl, 'http')) {$this->stateInfo = $this->getStateInfo('ERROR_HTTP_LINK');return;}//获取请求头并检测死链$heads = get_headers($imgUrl, 1);if (!(stristr($heads[0], '200') && stristr($heads[0], 'OK'))) {$this->stateInfo = $this->getStateInfo('ERROR_DEAD_LINK');return;}//格式验证(扩展名验证和Content-Type验证)$fileType = strtolower(strrchr($imgUrl, '.'));if (!in_array($fileType, $this->config['allowFiles']) || !isset($heads['Content-Type']) || !stristr($heads['Content-Type'], 'image')) {$this->stateInfo = $this->getStateInfo('ERROR_HTTP_CONTENTTYPE');return;}//打开输出缓冲区并获取远程图片ob_start();$context = stream_context_create(['http' => ['follow_location' => false, // don't follow redirects]]);readfile($imgUrl . '?' . $queryString, false, $context);$img = ob_get_contents();ob_end_clean();preg_match('/[\\/]([^\\/]*)[\\.]?[^\\.\\/]*$/', $imgUrl, $m);$this->oriName = $m ? $m[1] : '';$this->fileSize = strlen($img);$this->fileType = $this->getFileExt();$this->fullName = $this->getFullName();$this->filePath = $this->getFilePath();$this->fileName = $this->getFileName();$dirname = dirname($this->filePath);//检查文件大小是否超出限制if (!$this->checkSize()) {$this->stateInfo = $this->getStateInfo('ERROR_SIZE_EXCEED');return;}//检查文件内容是否真的是图片if ('image' != substr(mime_content_type($this->filePath), 0, 5)) {$this->stateInfo = $this->getStateInfo('ERROR_TYPE_NOT_ALLOWED');return;}$obj = $this->ossService->putObject($this->fullName, base64_encode($img));if ($obj) {$this->fullName = $obj;$this->stateInfo = $this->stateMap[0];} else {$this->stateInfo = $this->getStateInfo('ERROR_WRITE_CONTENT');}}/*** 上传错误检查.** @param $errCode** @return string*/private function getStateInfo($errCode){return !$this->stateMap[$errCode] ? $this->stateMap['ERROR_UNKNOWN'] : $this->stateMap[$errCode];}/*** 获取文件扩展名.** @return string*/private function getFileExt(){return strtolower(strrchr($this->oriName, '.'));}/*** 重命名文件.** @return string*/private function getFullName(){//替换日期事件$t = time();$d = explode('-', date('Y-y-m-d-H-i-s'));$format = $this->config['pathFormat'];$format = str_replace('{yyyy}', $d[0], $format);$format = str_replace('{yy}', $d[1], $format);$format = str_replace('{mm}', $d[2], $format);$format = str_replace('{dd}', $d[3], $format);$format = str_replace('{hh}', $d[4], $format);$format = str_replace('{ii}', $d[5], $format);$format = str_replace('{ss}', $d[6], $format);$format = str_replace('{time}', $t, $format);//过滤文件名的非法字符,并替换文件名$oriName = substr($this->oriName, 0, strrpos($this->oriName, '.'));$oriName = preg_replace('/[\\|\\?"\\<\\>\\/\\*\\\\]+/', '', $oriName);$format = str_replace('{filename}', $oriName, $format);//替换随机字符串$randNum = mt_rand(1, 1000000000) . mt_rand(1, 1000000000);if (preg_match('/\\{rand\\:([\\d]*)\\}/i', $format, $matches)) {$format = preg_replace('/\\{rand\\:[\\d]*\\}/i', substr($randNum, 0, $matches[1]), $format);}if ($this->fileType) {$ext = $this->fileType;} else {$ext = $this->getFileExt();}return $format . $ext;}/*** 获取文件名.** @return string*/private function getFileName(){return substr($this->filePath, strrpos($this->filePath, '/') + 1);}/*** 获取文件完整路径.** @return string*/private function getFilePath(){$fullname = $this->fullName;$rootPath = $_SERVER['DOCUMENT_ROOT'];if ('/' != substr($fullname, 0, 1)) {$fullname = '/' . $fullname;}return $rootPath . $fullname;}/*** 文件类型检测.** @return bool*/private function checkType(){return in_array($this->getFileExt(), $this->config['allowFiles']);}/*** 文件大小检测.** @return bool*/private function checkSize(){return $this->fileSize <= ($this->config['maxSize']);}/*** 获取当前上传成功文件的各项信息.** @return array*/public function getFileInfo(){return ['state' => $this->stateInfo,'url' => $this->fullName,'title' => $this->fileName,'original' => $this->oriName,'type' => $this->fileType,'size' => $this->fileSize,];}/*** 列出文件列表.** @param $start* @param $size* @param $ueditor_config_path** @return array*/public static function listFiles($start, $size, $ueditor_config_path){$path = preg_replace('/\\{.*\\}*/', '', config('ueditor.' . $ueditor_config_path));$oss = new OssService();$directorys = $oss->getFilesWithDirectory($path);$list = [];foreach ($directorys as $key=>$directory) {if ($key < $start - 1) {continue;}$list[] = ['url' => $oss->getUrl($directory['key']),'mtime'=> Carbon::parse($directory['last_modified'])->timestamp,];if (count($list) >= $size) {break;}}return ['state' => 'SUCCESS','list' => $list,'start' => $start,'total' => count($directorys),];}}
其中cos上传的service
namespace App\Http\Service;use Illuminate\Http\UploadedFile;use Qcloud\Cos\Client;class OssService extends BaseService{public $url;/*** 已更改为 tencent cos 沿用以前命名匹配代码** @var object*/public $ossClient;public function __construct(){$this->url = 'https://' . env('COSV5_BUCKET') . '.cos.' . env('COSV5_REGION') . '.myqcloud.com/';$this->ossClient = new Client(['region' => env('COSV5_REGION'),'schema' => env('COSV5_SCHEME', 'https'),'credentials' => ['secretId' => env('COSV5_SECRET_ID'),'secretKey' => env('COSV5_SECRET_KEY'),],]);parent::__construct();}/*** 根据文件/文件夹获取文件(包含路径).** @param $directory** @return array*/public function getFilesWithDirectory($directory,$marker = ''){$directory = preg_replace("/^\//",'',$directory);$result = [];$param = ['Bucket' => env('COSV5_BUCKET'),'Prefix' => $directory,'MaxKeys' => 1000,];while (true) {if ($marker) {$param['Marker'] = $marker;}$chunk_result = $this->ossClient->listObjects($param)->toArray();if (isset($chunk_result['Contents'])) {foreach ($chunk_result['Contents'] as $file){if(preg_match("/\/$/",$file['Key'])){continue;}$result[] = ['key'=>$file['Key'],'last_modified'=>$file['LastModified']];}$marker = end($chunk_result['Contents'])['Key'];} else {break;}}return $result;}/*** 上传文件** @param $ossPath* @param $realPath* @param bool $is_path* @param mixed $files** @return mixed*/public function put($ossPath, $files){try {// 请求成功if ($files instanceof UploadedFile) {$upload = $this->ossClient->upload(env('COSV5_BUCKET'), $ossPath, $files->openFile('rb'));} else {$upload = $this->ossClient->upload(env('COSV5_BUCKET'), $ossPath, $files);}$data = $upload->toArray();return env('COSV5_SCHEME') . '://' . $data['Location'];} catch (\Exception $e) {// 请求失败return false;}}/*** 上传文件对象** @param $ossPath* @param $object* @param string $style** @return bool|mixed*/public function putObject($ossPath, $object, $style = ''){try {// 请求成功$upload = $this->ossClient->Upload(env('COSV5_BUCKET'), $ossPath, $object);$ossPath = $style ? ($ossPath . $style) : $ossPath;return $this->getUrl($ossPath);} catch (\Exception $e) {// 请求失败return false;}}/*** 获取远程OSS文件对象** @param $ossPath** @return mixed*/public function getObjectByPath($ossPath){try {// 请求成功$result = $this->ossClient->getObject(['Bucket' => env('COSV5_BUCKET'),'Key' => $ossPath,]);return ($result->toArray())['Body'];} catch (\Exception $e) {// 请求失败return '';}}/*** 检查文件是否存在.** @param $ossPath** @return mixed*/public function exists($ossPath){try {$result = $this->ossClient->headObject(['Bucket' => env('COSV5_BUCKET'),'Key' => $ossPath,]);return true;} catch (\Exception $e) {return false;}}/*** 删除文件.** @param array|string $ossPath** @return mixed*/public function delete($ossPath){if (!$this->exists($ossPath)) {return false;}try {$result = $this->ossClient->deleteObject(['Bucket' => env('COSV5_BUCKET'),'Key' => $ossPath,]);// 请求成功return true;} catch (\Exception $e) {// 请求失败return false;}}/*** 删除文件夹.** @param $directory** @return mixed*/public function deleteDirectory($directory){//矫正文件格式$directory_end_style = strrpos($directory, '/');if (!$directory_end_style || $directory_end_style != strlen($directory) - 1) {$directory .= '/';}return $this->delete($directory);}/*** 获取远程URL地址** @param $ossPath* @param $strict:true从远程获取URL地址,false使用本地逻辑生成** @return mixed*/public function getUrl($ossPath, $strict = false){return env('COSV5_SCHEME') . '://' . env('COSV5_BUCKET') . '.cos.' . env('COSV5_REGION') . '.myqcloud.com/' . $ossPath;}/*** 获取签名url地址** @param $ossPath* @param mixed $valid_time* @param mixed $check_exists** @return mixed*/public function getSignUrl($ossPath, $valid_time = 3600, $check_exists = true){if ($check_exists && !$this->exists($ossPath)) {return '';}try {// 请求成功return $this->ossClient->getObjectUrl(env('COSV5_BUCKET'), $ossPath, '+' . ceil($valid_time / 60) . ' minutes');// if(preg_match("/^http:/", $url)){// $url = preg_replace("/^http:/", 'https:', $url);// }} catch (\Exception $e) {// 请求失败return '';}}/*** 将其他服务器上的文件上传到OSS中.** @param $ossPath* @param $remotePath** @return bool|mixed|string*/public function putRemoteFile($ossPath, $remotePath){$client = new \GuzzleHttp\Client();$res = $client->get($remotePath, ['verify'=> false,]);$base_img = $res->getBody()->getContents();return $this->putObject($ossPath, $base_img);}}
需要用的类包
qcloud/cos-sdk-v5nesbot/carbon