插件官网:GitHub - hbh112233abc/ueditor: thinkphp6 ueditor plugin
一、安装
composer require bingher/ueditor
二、资源及数据库配置
php think ueditor:publish
php think migrate:run
在命令运行,只要生成配置表,会自动在你数据库中插入一个ueditor_config表
三、网页html中引用(引入多个)
3.1 html代码
<script id="content" type="text/plain" style="width:100%;height:500px;">{notempty name="$rs.content"}{$rs.content|raw}{else/}详情{/notempty}</script>
3.2 js代码
与上面①②对应,保持一样的名称
<!--百度编辑器 开始-->
<script type="text/javascript" charset="utf-8" src="__STATIC__/bingher/ueditor/ueditor.config.js"></script>
<script type="text/javascript" charset="utf-8" src="__STATIC__/bingher/ueditor/ueditor.all.js"> </script>
<!--建议手动加在语言,避免在ie下有时因为加载语言失败导致编辑器加载失败-->
<!--这里加载的语言文件会覆盖你在配置项目里添加的语言类型,比如你在配置项目里配置的是英文,这里加载的中文,那最后就是中文-->
<script type="text/javascript" charset="utf-8" src="__STATIC__/bingher/ueditor/lang/zh-cn/zh-cn.js"></script>
<script type="text/javascript">
var pc_ue = UE.getEditor('onlineservice_invitation_content_pc', {
initialFrameHeight:100,
autoHeightEnabled: true,
zIndex: 100,
autoFloatEnabled: true
,toolbars: [[
'fullscreen', 'source', '|', 'undo', 'redo', '|',
'bold', 'italic', 'underline', 'fontborder', 'strikethrough', 'superscript', 'subscript', 'removeformat', 'formatmatch', 'autotypeset', 'blockquote', 'pasteplain', '|', 'forecolor', 'backcolor', 'insertorderedlist', 'insertunorderedlist', 'selectall', 'cleardoc', '|',
// 'rowspacingtop', 'rowspacingbottom', 'lineheight', '|',
'customstyle', 'paragraph', 'fontfamily', 'fontsize', '|',
// 'directionalityltr', 'directionalityrtl', 'indent', '|',
'justifyleft', 'justifycenter', 'justifyright', 'justifyjustify', '|', 'touppercase', 'tolowercase', '|',
// 'link', 'unlink', 'anchor', '|', 'imagenone', 'imageleft', 'imageright', 'imagecenter', '|',
// 'simpleupload', 'insertimage', 'emotion', 'scrawl', 'insertvideo', 'music', 'attachment', 'map', 'gmap', 'insertframe', 'insertcode', 'webapp', 'pagebreak', 'template', 'background', '|',
// 'horizontal', 'date', 'time', 'spechars', 'snapscreen', 'wordimage', '|',
// 'inserttable', 'deletetable', 'insertparagraphbeforetable', 'insertrow', 'deleterow', 'insertcol', 'deletecol', 'mergecells', 'mergeright', 'mergedown', 'splittocells', 'splittorows', 'splittocols', 'charts', '|',
// 'print', 'preview', 'searchreplace', 'drafts', 'help'
]]
// ,initialContent: "<p>请在这里输入正文...</p>"//默认内容
,elementPathEnabled: false//元素路径
,wordCount: true//字数统计
,enableAutoSave: false //自动保存
,autoSyncData: false//自动同步编辑器要提交的数据
,autoFloatEnabled:false//工具栏悬浮
,enableContextMenu:false //右键菜单
,lineheight:['1', '1.5','1.75','2', '3', '4', '5']//行高
,pasteplain:true //是否默认为纯文本粘贴
,catchRemoteImageEnable:false//远程图片抓取关闭
,imagePopup:false//图片操作的浮层开关,默认打开
});
var h5_ue = UE.getEditor('onlineservice_invitation_content_h5', {
initialFrameHeight:100,
autoHeightEnabled: true,
zIndex: 100,
autoFloatEnabled: true
,toolbars: [[
'fullscreen', 'source', '|', 'undo', 'redo', '|',
'bold', 'italic', 'underline', 'fontborder', 'strikethrough', 'superscript', 'subscript', 'removeformat', 'formatmatch', 'autotypeset', 'blockquote', 'pasteplain', '|', 'forecolor', 'backcolor', 'insertorderedlist', 'insertunorderedlist', 'selectall', 'cleardoc', '|',
// 'rowspacingtop', 'rowspacingbottom', 'lineheight', '|',
'customstyle', 'paragraph', 'fontfamily', 'fontsize', '|',
// 'directionalityltr', 'directionalityrtl', 'indent', '|',
'justifyleft', 'justifycenter', 'justifyright', 'justifyjustify', '|', 'touppercase', 'tolowercase', '|',
// 'link', 'unlink', 'anchor', '|', 'imagenone', 'imageleft', 'imageright', 'imagecenter', '|',
// 'simpleupload', 'insertimage', 'emotion', 'scrawl', 'insertvideo', 'music', 'attachment', 'map', 'gmap', 'insertframe', 'insertcode', 'webapp', 'pagebreak', 'template', 'background', '|',
// 'horizontal', 'date', 'time', 'spechars', 'snapscreen', 'wordimage', '|',
// 'inserttable', 'deletetable', 'insertparagraphbeforetable', 'insertrow', 'deleterow', 'insertcol', 'deletecol', 'mergecells', 'mergeright', 'mergedown', 'splittocells', 'splittorows', 'splittocols', 'charts', '|',
// 'print', 'preview', 'searchreplace', 'drafts', 'help'
]]
// ,initialContent: "<p>请在这里输入正文...</p>"//默认内容
,elementPathEnabled: false//元素路径
,wordCount: true//字数统计
,enableAutoSave: false //自动保存
,autoSyncData: false//自动同步编辑器要提交的数据
,autoFloatEnabled:false//工具栏悬浮
,enableContextMenu:false //右键菜单
,lineheight:['1', '1.5','1.75','2', '3', '4', '5']//行高
,pasteplain:true //是否默认为纯文本粘贴
,catchRemoteImageEnable:false//远程图片抓取关闭
,imagePopup:false//图片操作的浮层开关,默认打开
});
</script>
<!--百度编辑器 结束-->
3.3 JS取值
var onlineservice_invitation_content_pc = pc_ue.getContent();
var onlineservice_invitation_content_h5 = h5_ue.getContent();
与上面①②对应,保持一样的名称
四、配置与使用
1、打开下面这两个网址,看看是否配置正常
http://你的域名/ueditor/demo/view
http://你的域名/ueditor/demo/setting
打开显示错误:
ueditor [ue_setting] bingher ueditor demo 配置信息表不存在
2、需要修改文件:Setting.php UeConfig.php【官方已经修正】
/vendor/bingher/ueditor/src/controller/Setting.php
/vendor/bingher/ueditor/src/config/UeConfig.php
文件中的ueditor_config连接方式
官方默认是用:
$config = Db::table('ueditor_config')->column('value,remark', 'name');
但是我系统是加前缀的,所以需要修改
$config = Db::name('ueditor_config')->column('value,remark', 'name');
还要修改下面两个地方
刷新正常
2、修改接授网址:public/static/bingher/ueditor/ueditor.config.js【官方已经修正】
serverUrl: "/ueditor/index"
四、配置登录权限(安全设置)
http://你的域名/ueditor/demo/view
http://你的域名/ueditor/demo/setting
这两个网址如果不设置权限,那么谁都可以随便修改
官方规定:关闭APP_DEBUG(生产环境)后,默认会验证session(session_uid_key)是否有值,如果配置你的系统不是存储session('uid'),请自行配置对应的用于验证权限的键名,默认验证方法是check_uid
打开http://你的域名/ueditor/demo/setting
修改成为自己系统的一样
五、上传到七牛、阿里OSS第三方云
首先安装 TP6 第三方filesystem扩展包(支持阿里云、七牛云)thans/thinkphp-filesystem-cloud ——https://gitee.com/thans/thinkphp-filesystem-cloud
安装和作用教程:tp6 第三方filesystem扩展包(thans/thinkphp-filesystem-cloud)(支持阿里云OSS和七牛和腾讯云COS) 使用教程调用手册_php菜鸟技术天地-CSDN博客
只要修改文件:/vendor/bingher/ueditor/src/controller/Ueditor.php
$this->fs = $this->config->initFilesystem();
//读取系统缓存,//判断是上传到哪里 0=服务器本身,1=七牛 2=阿里OSS 3=腾讯
$systemConfigArr=[];$systemConfig=[];$system_upload_type=0;$path='';
$systemConfig=Cache::get('systemConfig');
if(empty($systemConfig)){
//重新生成缓存
$CacheData = new CacheModel();
$systemConfig = $CacheData -> makeSystemData();
}
$systemConfigArr=json_decode($systemConfig,true);
// var_dump($systemConfigArr['fieldValueAll']);die;
$system_upload_type=$systemConfigArr['fieldValueAll']['system_upload_type'];
$this->system_upload_type = $system_upload_type;
switch ($system_upload_type)
{
case 3:
$Filesystem='qcloud';
break;
case 2:
$Filesystem='aliyun';
break;
case 1:
$Filesystem='qiniu';
break;
default:
$Filesystem='';
}
if($system_upload_type>0){//第三方
$path=$Filesystem.'/';
//取得第三方的访问域名
$this-> ossurl = '';
$fsConfig = Config::get('filesystem');
// var_dump($fsConfig['disks'][$Filesystem]['url']);die;
if (!empty($fsConfig['disks'][$Filesystem])) {
$this-> ossurl = $fsConfig['disks'][$Filesystem]['url'];
// var_dump($this->ossurl);die;
}
$this->fs = $this->config->initFilesystem($Filesystem);
}else{//本地
$this->fs = $this->config->initFilesystem();
}
$Filesystem=第三方的标识 /config/filesystem.php
当然了还要修改显示路径
$this->savePath = path_join($path.'ueditor', $this->uid);
再修改下上传文件格式和大小
搞定收工
保存远程图片到OSS上,默认是保存服务器上的
修改后的function _saveRemot()代码:
/**
* 拉取远程图片
*
* @param array $config 配置信息
* @param string $fieldName 字段名
*
* @return mixed
*/
private function _saveRemote($config, $fieldName)
{
$imgUrl = htmlspecialchars($fieldName);
$imgUrl = str_replace("&", "&", $imgUrl);
//http开头验证
if (strpos($imgUrl, "http") !== 0) {
$this->error = '链接不是http|https链接';
return false;
}
//获取请求头并检测死链
$heads = get_headers($imgUrl, true);
if (!(stristr($heads[0], "200") && stristr($heads[0], "OK"))) {
$this->error = '链接不可用';
return false;
}
//格式验证(扩展名验证和Content-Type验证)
$fileType = strtolower(strrchr(strrchr($imgUrl, '/'), '.'));
//img链接后缀可能为空,Content-Type须为image
if ((!empty($fileType) && !in_array($fileType, $config['allowFiles']))
|| stristr($heads['Content-Type'], "image") === -1
) {
$this->error = '链接contentType不正确';
return false;
}
//解析出域名作为http_referer
$urlArr = explode('/', $imgUrl);
$protocol = str_replace(':', '', $urlArr[0]);
$httpReferer = $protocol . ':' . '//' . $urlArr[2];
//打开输出缓冲区并获取远程图片
ob_start();
$context = stream_context_create(
[
'http' => array(
//'header' => "Referer:$httpReferer", //突破防盗链,不可用
'user_agent' => 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36', //突破防盗链
'follow_location' => false, // don't follow redirects
),
]
);
$res = false;
$message = '';
try {
$res = readfile($imgUrl, false, $context);
} catch (\Exception $e) {
$message = $e->getMessage();
}
$img = ob_get_contents();
ob_end_clean();
if ($res === false) {
$this->error = $message;
return false;
}
preg_match("/[\/]([^\/]*)[\.]?[^\.\/]*$/", $imgUrl, $fileName);
$savePath = path_join($this->savePath, date('Ymd'));
$dirname = path_join($this->rootPath, $savePath);
$file['oriName'] = $fileName ? $fileName[1] : "";
$file['filesize'] = strlen($img);
$file['ext'] = strtolower(strrchr($config['oriName'], '.'));
$file['name'] = uniqid() . $file['ext'];
$file['fullName'] = $dirname . $file['name'];
$fullName = $file['fullName'];
//检查文件大小是否超出限制
if ($file['filesize'] >= ($config["maxSize"])) {
$this->error = '文件大小超出网站限制';
return false;
}
//创建目录失败
$dirname='./upload/'.$this->savePath;
if (!file_exists($dirname) && !mkdir($dirname, 0777, true)) {
$this->error = '目录创建失败';
return false;
} else if (!is_writeable($dirname)) {
$this->error = '目录没有写权限';
return false;
}
//移动文件
if (!(file_put_contents($fullName, $img) && file_exists($fullName))) {
//移动失败
$this->error = '写入文件内容错误';
return false;
}
$url='/upload/'.$savePath.$file['name'];
//20210603 上传到OSS
$CommonModel = new CommonModel();
$ossImgUrl=$savePath.$file['name'];
$ecsPath='/upload/'.$savePath.$file['name'];
$OssData=$CommonModel -> customUploadOss($ossImgUrl,$ecsPath,$file['ext'],$file['name']);
if($OssData['code']!=200 || empty($OssData['data']['img'])){
$this->error = '上传OSS失败';
}
$url=$OssData['data']['img'];
// dump($OssData);
//删除刚刚上传到ECS服务器的图片文件,只保留OSS上的文件
$delIs=unlink($fullName);
if(!$delIs){
$this->error = '删除图片失败';
}
//移动成功
$data = array(
'state' => 'SUCCESS',
'url' => $url,
// 'url' => path_join($this->urlPath, $savePath, $file['name']),
'title' => $file['name'],
'original' => $file['oriName'],
'type' => $file['ext'],
'size' => $file['filesize'],
);
return $data;
}
加入
//20210603 上传到OSS
$CommonModel = new CommonModel();
$ossImgUrl=$savePath.$file['name'];
$ecsPath='/upload/'.$savePath.$file['name'];
$OssData=$CommonModel -> customUploadOss($ossImgUrl,$ecsPath,$file['ext'],$file['name']);
if($OssData['code']!=200 || empty($OssData['data']['img'])){
$this->error = '上传OSS失败';
}
$url=$OssData['data']['img'];
// dump($OssData);
//删除刚刚上传到ECS服务器的图片文件,只保留OSS上的文件
$delIs=unlink($fullName);
if(!$delIs){
$this->error = '删除图片失败';
}
customUploadOss()代码
安装了第三方filesystem扩展包(支持阿里云、七牛云)thans/thinkphp-filesystem-cloud ——https://gitee.com/thans/thinkphp-filesystem-cloud
引入:use OSS\OssClient;
/**
*Description: 自定义上传远程图片到第三方OSS、本地(先下载到自己服务器再上传到OSS然后再删除自己服务器上的图片)
*Author:
*Date:2021-06-02
*Param:$ossUrlImgName str/必填 保存图片到OSS上的完整路径 oss:ueditor/20210603/2021060360b84f39c0418.png
*Param:$ecsPath str/必填 图片下载到自己服务器上的路径-相对路径:./upload/ueditor/20210603/2021060360b84f39c0418.png
*Param:$ext str/必填 图片后缀 : .png
*Param:$imgName str/必填 图片名称 : 60b84f39c0418.png
*Param:$imgUrl str/必填 图片路径(可以是本地电脑的图片路径(/sdfk/01.jpg)也可以是远程图片完整网址) 远程图片完整网址例子:https://static.cnbetacdn.com/article/2021/06/5311492ac72d54f.png
*Ruturn:
*/
public function customUploadOss($ossUrlImgName,$ecsPath,$imgName=null,$ext=null,$imgUrl=null){
// $imgUrl='https://static.cnbetacdn.com/article/2021/06/5311492ac72d54f.png';
$data=[];$img_url=null;$code=200;$msg='成功';
$systemConfigArr=[];$systemConfig=[];$system_upload_type=0;$path='';$system_website=null;
$systemConfig=Cache::get('systemConfig');
if(empty($systemConfig)){
//重新生成缓存
$CacheData = new CacheModel();
$systemConfig = $CacheData -> makeSystemData();
}
$systemConfigArr=json_decode($systemConfig,true)['fieldValueAll'];
// dump($systemConfigArr);die;
$system_website = $systemConfigArr['system_website'];//
$system_upload_type=$systemConfigArr['system_upload_type'];
try {
if(empty($ossUrlImgName)){
throw new \Exception('图片服务器路径为空');
}
if(empty($ecsPath)){
throw new \Exception('图片OSS路径为空');
}
if(empty($imgName)){
throw new \Exception('图片名称为空');
}
if(empty($ext)){
throw new \Exception('图片后缀为空');
}
if(empty($systemConfigArr)){
throw new \Exception('系统配置缓存为空');
}
if($system_upload_type==1){//1=七牛
}elseif($system_upload_type==2){//2=阿里OSS
// 阿里云主账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM账号进行API访问或日常运维,请登录RAM控制台创建RAM账号。
$accessKeyId = $systemConfigArr['oss_aliyun_accessKey'];
$accessKeySecret = $systemConfigArr['oss_aliyun_secretKey'];
$endpoint = $systemConfigArr['oss_aliyun_Endpoint'];// Endpoint(地域节点) oss-cn-shenzhen.aliyuncs.com
$bucket= $systemConfigArr['oss_aliyun_storage_name'];// 设置存储空间名称。
if(empty($accessKeyId) || !$accessKeySecret || !$endpoint|| !$bucket){
throw new \Exception('阿里云OSS必填参数为空');
}
$ossClient = new OssClient($accessKeyId,$accessKeySecret,$endpoint);
$ecsPath='.'.$ecsPath;
$result = $ossClient->uploadFile($bucket, $ossUrlImgName, $ecsPath);
// dump($result);
if(!$result || empty($result['info']['url'])){
throw new \Exception('上传失败');
}
$img_url=!empty($result['info']['url'])?$result['info']['url']:'';
}elseif($system_upload_type==3){//3=腾讯
}else{//0=服务器本身
$img_url=$system_website.$ecsPath;
}
if(!$img_url){
throw new \Exception('完整图片网址为空');
}
$data['img']=$img_url;//图片完整网址
} catch (\Exception $e) {
// 这是进行异常捕获
$code=-200;$msg=$e->getMessage();
}
return ['code' => $code,'msg' =>$msg,'data' =>$data];
}
最后给出完整的修改文件Ueditor.php代码:(\vendor\bingher\ueditor\src\controller\Ueditor.php)
<?php
namespace bingher\ueditor\controller;
use think\facade\Request;
use think\Image;
//20210601 上传到第三方
use think\Facade\Cache;
use app\admin\model\CacheModel;
use think\facade\Config;
use app\admin\model\CommonModel;
/**
* Ueditor后端服务接口
*/
class Ueditor extends Base
{
protected $uid;
protected $error;
protected $upfile;
protected $fs;
/**
* 构造函数
*
* @return self
*/
public function __construct()
{
parent::__construct();
//20210601 上传到第三方 读取系统缓存,//判断是上传到哪里 0=服务器本身,1=七牛 2=阿里OSS 3=腾讯
$systemConfigArr=[];$systemConfig=[];$system_upload_type=0;$path='';
$systemConfig=Cache::get('systemConfig');
if(empty($systemConfig)){
//重新生成缓存
$CacheData = new CacheModel();
$systemConfig = $CacheData -> makeSystemData();
}
$systemConfigArr=json_decode($systemConfig,true);
// var_dump($systemConfigArr['fieldValueAll']);die;
$system_upload_type=$systemConfigArr['fieldValueAll']['system_upload_type'];
$this->system_img_format=explode(',',$systemConfigArr['fieldValueAll']['system_img_format']);//允许图片上传格式
$this->system_img_size=$systemConfigArr['fieldValueAll']['system_img_size']*1024;//允许图片上传大小
$this->system_file_format=explode(',',$systemConfigArr['fieldValueAll']['system_file_format']);//上传文件格式
$this->system_file_size=$systemConfigArr['fieldValueAll']['system_file_size']*1024;//上传文件大小
$this->system_upload_type = $system_upload_type;
switch ($system_upload_type)
{
case 3:
$Filesystem='qcloud';
break;
case 2:
$Filesystem='aliyun';
break;
case 1:
$Filesystem='qiniu';
break;
default:
$Filesystem='';
}
$this->Filesystem = $Filesystem;
if($system_upload_type>0){//第三方
$path=$Filesystem.'/';
//取得第三方的访问域名
$this-> ossurl = '';
$fsConfig = Config::get('filesystem');
// var_dump($fsConfig['disks'][$Filesystem]['url']);die;
if (!empty($fsConfig['disks'][$Filesystem])) {
$this-> ossurl = $fsConfig['disks'][$Filesystem]['url'];
// var_dump($this->ossurl);die;
}
$this->fs = $this->config->initFilesystem($Filesystem);
}else{//本地
$this->fs = $this->config->initFilesystem();
}
$uidKey = $this->config->get('session_uid_key', 'uid');
$this->uid = session($uidKey) ? strval(session($uidKey)) : '';
$this->upField = $this->config->get('upload_field_name', 'upfile');
// $this->fs = $this->config->initFilesystem();
$this->rootPath = $this->config->get('filesystem_root');
$this->urlPath = $this->config->get('filesystem_url');
$this->savePath = path_join('ueditor', date('Ymd'));
// $this->savePath = path_join('ueditor', $this->uid);
}
/**
* 针对ueditor的请求链接
* 因为注册了路由,请求路径为 http://domain/ueditor/index
* @return \think\Response
*/
public function index()
{
$action = Request::param('action', '');
switch ($action) {
case 'config':
return $this->config->json();
break;
/* 上传图片 */
case 'upload_image':
$config = [
// "maxSize" => $this->config->get('imageMaxSize'),
// "allowFiles" => $this->config->get('imageAllowFiles'),
"maxSize" => $this->system_img_size,
"allowFiles" => $this->system_img_format,
];
$result = $this->_upFile($config);
break;
/* 上传涂鸦 */
case 'upload_scrawl':
$config = [
"maxSize" => $this->config->get('scrawlMaxSize'),
"allowFiles" => $this->config->get('scrawlAllowFiles'),
"oriName" => "scrawl.png",
];
$result = $this->_upBase64($config);
break;
/* 上传视频 */
case 'upload_video':
$config = [
// "maxSize" => $this->config->get('videoMaxSize'),
// "allowFiles" => $this->config->get('videoAllowFiles'),
"maxSize" => $this->system_file_size,
"allowFiles" => $this->system_file_format,
];
$result = $this->_upFile($config);
break;
/* 上传音频 */
case 'upload_audio':
$config = [
// "maxSize" => $this->config->get('audioMaxSize'),
// "allowFiles" => $this->config->get('audioAllowFiles'),
"maxSize" => $this->system_file_size,
"allowFiles" => $this->system_file_format,
];
$result = $this->_upFile($config);
break;
/* 上传文件 */
case 'upload_file':
// default:
$config = [
// "maxSize" => $this->config->get('fileMaxSize'),
// "allowFiles" => $this->config->get('fileAllowFiles'),
"maxSize" => $this->system_file_size,
"allowFiles" => $this->system_file_format,
];
$result = $this->_upFile($config);
break;
/* 列出图片 */
case 'list_image':
$allowFiles = $this->config->get('imageManagerAllowFiles');
$listSize = $this->config->get('imageManagerListSize');
$path = $this->config->get('imageManagerListPath');
$get = Request::param();
$result = $this->_fileList($allowFiles, $listSize, $get);
break;
/* 列出文件 */
case 'list_file':
$allowFiles = $this->config->get('fileManagerAllowFiles');
$listSize = $this->config->get('fileManagerListSize');
$path = $this->config->get('fileManagerListPath');
$get = Request::param();
$result = $this->_fileList($allowFiles, $listSize, $get);
break;
/* 抓取远程文件 */
case 'catch_image':
$config = [
"pathFormat" => $this->config->get('catcherPathFormat'),
"maxSize" => $this->config->get('catcherMaxSize'),
"allowFiles" => $this->config->get('catcherAllowFiles'),
"oriName" => "remote.png",
];
/* 抓取远程图片 */
$list = [];
$failList = []; //错误的列表
$source = Request::param($this->upField);
if (empty($source)) {
return $this->error('参数错误');
}
foreach ($source as $imgUrl) {
$remoteResult = $this->_saveRemote($config, $imgUrl);
if ($remoteResult === false) {
array_push($failList, [
"state" => $this->error,
"source" => htmlspecialchars($imgUrl),
]);
} else {
array_push(
$list,
[
"state" => $remoteResult["state"],
"url" => $remoteResult["url"],
"size" => $remoteResult["size"],
"title" => htmlspecialchars($remoteResult["title"]),
"original" => htmlspecialchars($remoteResult["original"]),
"source" => htmlspecialchars($imgUrl),
]
);
}
}
$result = [
'state' => count($list) ? 'SUCCESS' : 'ERROR',
'list' => $list,
'fail_list' => $failList,
];
break;
default:
return $this->error('请求地址出错');
break;
}
/* 错误信息 */
if ($result === false) {
return $this->error($this->error);
}
$callback = Request::param('callback');
if (empty($callback)) {
return $this->success($result);
}
if (!preg_match('/^[\w_]+$/', $callback)) {
return $this->error('callback参数不合法');
}
return htmlspecialchars($callback . '(' . json_encode($result) . ')');
}
/**
* 上传文件的主处理方法
*
* @param array $config 配置信息
*
* @return array
*/
private function _upFile($config)
{
$file = request()->file($this->upField);
$check = $this->check($config, $file);
if ($check !== true) {
return $check;
}
// dump($file);
// dump($this->savePath);die;
$saveName = $this->fs->putFile($this->savePath, $file);
// dump($saveName);die;
if (!$saveName) {
$this->error = '文件上传失败';
return false;
}
$filePath = $this->fs->path($saveName);
$ext = $file->getExtension();
if ($this->isImage($ext)) {
try {
$this->imageHandle($filePath, $ext);
} catch (\Exception $e) {
$this->error = $e->getMessage();
return false;
}
}
//20210601 修改 第三方
$size=0;$url='';
// var_dump($this->urlPath);die;
if($this->system_upload_type>0){//第三方
$url = path_join('', $saveName);
$url = $this-> ossurl.$url;
}else{//0=本地服务器
$url = path_join($this->urlPath, $saveName);
}
// $size=$file->getSize();
$data = [
// 'url' => path_join($this->urlPath, $saveName),
'url' => $url,
'title' => $_FILES[$this->upField]['name'],
'original' => $_FILES[$this->upField]['name'],
'type' => '.' . $ext,
'size' => $file->getSize(),
];
return $data;
}
/**
* 图片再处理(压缩,水印)
*
* @param string $filePath 文件路径
* @param string $ext 文件后缀
*
* @return string 文件路径
*/
protected function imageHandle($filePath, $ext)
{
$thumbType = $this->config->get('thumb_type', 0);
//获取图片清晰度设置,默认是80
$quality = $this->config->get('image_upload_quality', 80);
//获取图片宽高的最大限制值,0为不限制
$maxLimit = $this->config->get('image_upload_max_limit', 680);
$image = Image::open($filePath);
if ($maxLimit > 0 && $thumbType > 0) {
//设置缩略图模式,按宽最大680或高最大680压缩
$image->thumb($maxLimit, $maxLimit, $thumbType);
}
if ($this->water == 1) {
$font = $this->config->get(
'water_font_path',
__DIR__ . '/../assets/zhttfs/1.ttf'
);
$image->text(
$this->config->get('water_text'),
$font,
10,
'#FFCC66',
$this->config->get('water_position'),
[-8, -8]
)->save($filePath, $ext, $quality);
} else if ($this->water == 2) {
$image->water(
$this->config->get('water_image'),
$this->config->get('water_position'),
80
)->save($filePath, $ext, $quality);
} else {
$image->save($filePath, $ext, $quality);
}
return $filePath;
}
/**
* 根据文件后缀判断是不是图片
*
* @param string $ext 文件后缀
*
* @return boolean
*/
protected function isImage($ext)
{
$imageExts = self::formatExts($this->config->get('imageAllowFiles', []));
return in_array($ext, $imageExts);
}
/**
* 处理base64编码的图片上传
*
* @param array $config 配置信息
*
* @return array
*/
private function _upBase64($config)
{
$base64Data = Request::post($this->upfile);
if (empty($base64Data)) {
$this->error = '参数数据为空';
return false;
}
$img = base64_decode($base64Data);
$savePath = $this->savePath;
$dirname = path_join($this->rootPath, $savePath);
$file['filesize'] = strlen($img);
$file['oriName'] = $config['oriName'];
$file['ext'] = strtolower(strrchr($config['oriName'], '.'));
$file['name'] = uniqid() . $file['ext'];
$file['fullName'] = path_join($dirname, $file['name']);
$fullName = $file['fullName'];
//检查文件大小是否超出限制
if ($file['filesize'] >= ($config["maxSize"])) {
$this->error = '文件大小超出网站限制';
return false;
}
//创建目录失败
if (!file_exists($dirname) && !mkdir($dirname, 0777, true)) {
$this->error = '目录创建失败';
return false;
} else if (!is_writeable($dirname)) {
$this->error = '目录没有写权限';
return false;
}
//移动文件
if (!(file_put_contents($fullName, $img) && file_exists($fullName))) {
//移动失败
$this->error = '写入文件内容错误';
return false;
}
//移动成功
$data = [
'url' => path_join($this->urlPath, $savePath, $file['name']),
'title' => $file['name'],
'original' => $file['oriName'],
'type' => $file['ext'],
'size' => $file['filesize'],
];
return $data;
}
/**
* 拉取远程图片
*
* @param array $config 配置信息
* @param string $fieldName 字段名
*
* @return mixed
*/
private function _saveRemote($config, $fieldName)
{
$imgUrl = htmlspecialchars($fieldName);
$imgUrl = str_replace("&", "&", $imgUrl);
//http开头验证
if (strpos($imgUrl, "http") !== 0) {
$this->error = '链接不是http|https链接';
return false;
}
//获取请求头并检测死链
$heads = get_headers($imgUrl, true);
if (!(stristr($heads[0], "200") && stristr($heads[0], "OK"))) {
$this->error = '链接不可用';
return false;
}
//格式验证(扩展名验证和Content-Type验证)
$fileType = strtolower(strrchr(strrchr($imgUrl, '/'), '.'));
//img链接后缀可能为空,Content-Type须为image
if ((!empty($fileType) && !in_array($fileType, $config['allowFiles']))
|| stristr($heads['Content-Type'], "image") === -1
) {
$this->error = '链接contentType不正确';
return false;
}
//解析出域名作为http_referer
$urlArr = explode('/', $imgUrl);
$protocol = str_replace(':', '', $urlArr[0]);
$httpReferer = $protocol . ':' . '//' . $urlArr[2];
//打开输出缓冲区并获取远程图片
ob_start();
$context = stream_context_create(
[
'http' => array(
//'header' => "Referer:$httpReferer", //突破防盗链,不可用
'user_agent' => 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36', //突破防盗链
'follow_location' => false, // don't follow redirects
),
]
);
$res = false;
$message = '';
try {
$res = readfile($imgUrl, false, $context);
} catch (\Exception $e) {
$message = $e->getMessage();
}
$img = ob_get_contents();
ob_end_clean();
if ($res === false) {
$this->error = $message;
return false;
}
preg_match("/[\/]([^\/]*)[\.]?[^\.\/]*$/", $imgUrl, $fileName);
$savePath = path_join($this->savePath, date('Ymd'));
$dirname = path_join($this->rootPath, $savePath);
$file['oriName'] = $fileName ? $fileName[1] : "";
$file['filesize'] = strlen($img);
$file['ext'] = strtolower(strrchr($config['oriName'], '.'));
$file['name'] = uniqid() . $file['ext'];
$file['fullName'] = $dirname . $file['name'];
$fullName = $file['fullName'];
//检查文件大小是否超出限制
if ($file['filesize'] >= ($config["maxSize"])) {
$this->error = '文件大小超出网站限制';
return false;
}
//创建目录失败
$dirname='./upload/'.$this->savePath;
if (!file_exists($dirname) && !mkdir($dirname, 0777, true)) {
$this->error = '目录创建失败';
return false;
} else if (!is_writeable($dirname)) {
$this->error = '目录没有写权限';
return false;
}
//移动文件
if (!(file_put_contents($fullName, $img) && file_exists($fullName))) {
//移动失败
$this->error = '写入文件内容错误';
return false;
}
$url='/upload/'.$savePath.$file['name'];
//20210603 上传到OSS
$CommonModel = new CommonModel();
$ossImgUrl=$savePath.$file['name'];
$ecsPath='/upload/'.$savePath.$file['name'];
$OssData=$CommonModel -> customUploadOss($ossImgUrl,$ecsPath,$file['ext'],$file['name']);
if($OssData['code']!=200 || empty($OssData['data']['img'])){
$this->error = '上传OSS失败';
}
$url=$OssData['data']['img'];
// dump($OssData);
//删除刚刚上传到ECS服务器的图片文件,只保留OSS上的文件
$delIs=unlink($fullName);
if(!$delIs){
$this->error = '删除图片失败';
}
//移动成功
$data = array(
'state' => 'SUCCESS',
'url' => $url,
// 'url' => path_join($this->urlPath, $savePath, $file['name']),
'title' => $file['name'],
'original' => $file['oriName'],
'type' => $file['ext'],
'size' => $file['filesize'],
);
return $data;
}
/**
* 文件列表
*
* @param array $allowFiles 指定的文件后缀数组
* @param int $listSize 列表分页数量
* @param array $get ['size'=>xxx,'start'=>xxx]
*
* @return array
*/
private function _fileList($allowFiles, $listSize, $get)
{
$dirname = path_join($this->rootPath, 'ueditor');
if ($this->uid != $this->config->get('super_admin_uid', 'admin')) {
$dirname = path_join($dirname, $this->uid);
}
$allowFiles = substr(str_replace(".", "|", join("", $allowFiles)), 1);
/* 获取参数 */
$size = isset($get['size']) ? htmlspecialchars($get['size']) : $listSize;
$start = isset($get['start']) ? htmlspecialchars($get['start']) : 0;
$end = $start + $size;
/* 获取文件列表 */
$files = $this->_getFiles($dirname, $allowFiles);
if (!count($files)) {
return [
"state" => "no match file",
"list" => [],
"start" => $start,
"total" => count($files),
];
}
/* 获取指定范围的列表 */
$len = count($files);
$list = [];
for ($i = min($end, $len) - 1; $i < $len && $i >= 0 && $i >= $start; $i--) {
$list[] = $files[$i];
}
/* 返回数据 */
$result = [
"state" => "SUCCESS",
"list" => $list,
"start" => $start,
"total" => count($files),
];
return $result;
}
/**
* 遍历获取目录下的指定类型的文件
*
* @param string $path 文件路径
* @param string $allowFiles 指定的文件后缀,以|分隔的文本
* @param array $files 文件数组
*
* @return array
*/
private function _getFiles($path, $allowFiles, &$files = [])
{
if (!is_dir($path)) {
return [];
}
if (substr($path, strlen($path) - 1) != '/') {
$path .= '/';
}
$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("/\.(" . $allowFiles . ")$/i", $file)) {
$files[] = array(
'url' => preg_replace(
'/(.*)upload/i',
'/upload',
$path2
),
'mtime' => filemtime($path2),
);
}
}
}
}
return $files;
}
/**
* [formatUrl 格式化url,用于将getFiles返回的文件路径进行格式化,起因是中文文件名的不支持浏览]
*
* @param array $files [文件数组]
*
* @return array [格式化后的文件数组]
*/
public static function formatUrl($files)
{
if (!is_array($files)) {
return $files;
}
foreach ($files as $key => $value) {
$data = [];
$data = explode('/', $value);
foreach ($data as $k => $v) {
if ($v != '.' && $v != '..') {
$data[$k] = urlencode($v);
$data[$k] = str_replace("+", "%20", $data[$k]);
}
}
$files[$key] = implode('/', $data);
}
return $files;
}
/**
* 格式化文件后缀
* 去除后缀名前面的.,例".exe"=>"exe"
*
* @param array $exts 文件后缀数组
*
* @return array
*/
public static function formatExts(array $exts): array
{
$data = [];
foreach ($exts as $key => $value) {
$data[] = ltrim($value, '.');
}
return $data;
}
/**
* 上传文件验证
* 1. 验证文件大小
* 2. 验证文件后缀
*
* @param array $config 配置信息
* @param Object $file 文件对象
*
* @return bool
*/
protected function check($config, $file)
{
try {
$rule = [
$this->upField => [
'fileSize' => $config['maxSize'],
'fileExt' => self::formatExts($config['allowFiles']),
],
];
validate($rule)->check([$this->upField => $file]);
return true;
} catch (\think\exception\ValidateException $e) {
$this->error = $e->getMessage();
return false;
}
}
}