本文主要的目的是为了让ueditor拥有前端直接oss上传图片的能力,不需要经过后端中转,减轻后端服务器的压力。同时添加springboot的支持。
注: 为了代码统一性和兼容性,本文所有编码方式均采用ES5。如果你觉得这没必要,可以使用ES6,甚至ES7的语法。
准备工作
-
ueditor source code
-
阿里云oss服务
-
一丢丢Node.js和grunt的知识
修改Ueditor源码
ueditor.config.js
这个文件会被原封不动打包且放到window中,我们需要往里面加一些oss的信息。
window.UEDITOR_CONFIG = {
...
//此处修改为获取json配置信息的网址(jsp改为springboot,稍后讲springboot的配置)
serverUrl: URL + "config"
oss: {
//获取oss签名的url
url: "http://localhost:8107/app/oss-signature",
//图片上传的路径
path: "/images/cms/"
}
}
_src/editor.js
增加oss上传
打开_src/editor.js,添加下面代码
UE.ossService = new OSSService();
function OSSService() {
this.ossSign = undefined;
this.getOSSSign = function(callback) {
var sign = this.ossSign;
var now = parseInt(new Date().getTime() / 1000);
if (sign && now - sign.expired <= 1800) {
callback(sign.data);
return;
}
var self = this;
//从服务器获取oss info
function fetchOSS() {
const url = window.UEDITOR_CONFIG.oss.url;
var request = new XMLHttpRequest();
request.onload = function(e) {
var res = JSON.parse(request.responseText);
if (!res || res.code != 0 || !res.data) {
alert('获取签名失败');
}
//回传oss sign
callback(res.data);
//缓存签名
self.ossSign = {
expired: parseInt(new Date().getTime() / 1000),
data: res.data
}
}
request.open('GET',url);
request.send();
}
fetchOSS();
}
this.uploadToOSS = function(sign,file,uploadCallback) {
function getOssFileName(file) {
var timestamp = new Date().getTime();
var random = parseInt(Math.random() * 1000000);
return timestamp + '_' + random + "." + file.ext;
}
var form = new FormData();
var path = window.UEDITOR_CONFIG.oss.path || '';
var key = path + "/" + getOssFileName(file);
// 添加签名信息
form.append('OSSAccessKeyId', sign['accessKeyId']);
form.append('policy', sign['policy']);
form.append('Signature', sign['signature']);
form.append('key', key);
// 添加文件
form.append('file', file);
var req = new XMLHttpRequest();
req.onload = function(e) {
var data = {
link: sign['baseUrl'] + '/' + key,
name: file.name
}
uploadCallback(data);
}
req.open('POST',sign['baseUrl']);
req.send(form);
}
}
_src/plugins/simpleupload.js
修改单图上传
打开_src/plugins/simpleupload.js,定位到113行左右,注释下面这段
//domUtils.on(iframe, 'load', callback);
//form.action = utils.formatUrl(imageActionUrl + (imageActionUrl.indexOf('?') == -1 ? '?':'&') + params);
//form.submit();
//替换为下面这段
var myService = UE.ossService;
var myFile = input['files'][0];
myService.getOSSSign(function(sign) {
myService.uploadToOSS(sign,myFile, function(data) {
loader = me.document.getElementById(loadingId);
loader.setAttribute('src', data.link);
loader.setAttribute('_src', data.link);
loader.setAttribute('title', data.name);
loader.setAttribute('alt', data.name);
loader.removeAttribute('id');
domUtils.removeClasses(loader, 'loadingclass');
});
});
同时,也可以把callback删了,当然留着也无妨,就是一些无用的代码而已。
_src/plugins/autoupload.js
拖拽上传
打开_src/plugins/autoupload.js,定位到打开88行
/* 创建Ajax并提交 */
var xhr = new XMLHttpRequest(),
fd = new FormData(),
params = utils.serializeParam(me.queryCommandValue('serverparam')) || '',
url = utils.formatUrl(actionUrl + (actionUrl.indexOf('?') == -1 ? '?':'&') + params);
删除这一整段XMLHttpRequest,添加下面oss上传
var myService = UE.ossService;
myService.getOSSSign(function(sign) {
myService.uploadToOSS(sign,file, function(data) {
successHandler({
"state": "SUCCESS",
"url": data.link,
"title": file.name,
"original": file.name,
"type": fileext,
"size": file['size']
});
});
});
dialogs/image/image.js
多图上传
打开dialogs/image/image.js,定位到大概744行
$upload.on('click', function () {
if ($(this).hasClass('disabled')) {
return false;
}
if (state === 'ready') {
uploader.upload();
} else if (state === 'paused') {
uploader.upload();
} else if (state === 'uploading') {
uploader.stop();
}
});
把上面代码改为下面oss上传
$upload.on('click', function () {
if ($(this).hasClass('disabled')) {
return false;
}
var start = function() {
if (state === 'ready') {
uploader.upload();
} else if (state === 'paused') {
uploader.upload();
} else if (state === 'uploading') {
uploader.stop();
}
}
var myService = UE.ossService;
myService.getOSSSign(function(sign) {
uploader['options']['server'] = sign['baseUrl'];
start();
});
});
uploadBeforeSend
定位到大约704行,找到下面代码
uploader.on('uploadBeforeSend', function (file, data, header) {
//这里可以通过data对象添加POST参数
header['X_Requested_With'] = 'XMLHttpRequest';
});
改为下面的代码
uploader.on('uploadBeforeSend', function (file, data, header) {
//这里可以通过data对象添加POST参数
header['X_Requested_With'] = 'XMLHttpRequest';
var myService = UE.ossService;
myService.getOSSSign(function(sign) {
var object_name = "images/" + new Date().getTime() + "_" + parseInt(Math.random() * 100000) + "." + file.file.ext;
data = $.extend(data, {
'OSSAccessKeyId': sign['accessKeyId'],
'policy': sign['policy'],
'Signature': sign['signature'],
'key': object_name
});
file.file.link = sign['baseUrl'] + '/' + object_name;
})
});
startUpload
定位到693行代码,删掉下面几行
var params = utils.serializeParam(editor.queryCommandValue('serverparam')) || '',
url = utils.formatUrl(actionUrl + (actionUrl.indexOf('?') == -1 ? '?':'&') + 'encode=utf-8&' + params);
uploader.option('server', url);
setState('uploading', files);
替换为
var myService = UE.ossService;
myService.getOSSSign(function(sign) {
uploader.option('server', sign['baseUrl']);
setState('uploading', files);
});
uploadSuccess
找到上传成功代码
uploader.on('uploadSuccess', function (file, ret) {
var $file = $('#' + file.id);
try {
var responseText = (ret._raw || ret),
json = utils.str2json(responseText);
if (json.state == 'SUCCESS') {
_this.imageList.push(json);
$file.append('<span class="success"></span>');
} else {
$file.find('.error').text(json.state).show();
}
} catch (e) {
$file.find('.error').text(lang.errorServerUpload).show();
}
});
修改为下面的的代码
uploader.on('uploadSuccess', function (file, ret) {
var $file = $('#' + file.id);
try {
var responseText = ret._raw;
if (responseText === '') {
let res = {
"state": "SUCCESS",
"url": file.link,
"title": file.name,
"original": file.name,
"type": file.ext,
"size": file.size
}
_this.imageList.push(res);
$file.append('<span class="success"></span>');
} else {
//todo
}
} catch (e) {
$file.find('.error').text(lang.errorServerUpload).show();
}
});
最后,找到
fileVal: editor.getOpt('imageFieldName'),
//替换为
fileVal: 'file'
打包代码
打包代码很简单,官网文档也有了一些简单的说明,这里不再赘述。不过提醒一下大家几个值得注意的地方,node版本最好不要过高,我使用node v8打包就报错。可以切换为node v4。推荐使用nvm管理node版本。
grunt –encode=utf8 –server=jsp
打包后,dist目录就是需要部署的代码。
修改jsp为SpringBoot
ueditor提供了jsp的方案,我们可以照葫芦画瓢,更改为spring即可。首先复制源码包jsp目录下src的代码到我们的项目,同时把jsp下的config.json复制到resources/ueditor目录。
config.json由百度的ConfigManager读取,需要修改一下新的路径。找到ConfigManager,修改
//String configContent = this.readFile( this.getConfigPath() );
InputStream is = this.getClass().getClassLoader().getResourceAsStream("ueditor/config.json");
添加下面项目依赖
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20180813</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.9</version>
</dependency>
新建一个Controller,添加如下配置
@Controller
public class UEditorController {
//注意,mapping必须和config.js中的server保持一致
@GetMapping(value = "/ueditor/config")
@ResponseBody
public String config(HttpServletRequest request) {
String rootPath = request.getSession().getServletContext().getRealPath("/");
return new ActionEnter(request, rootPath).exec();
}
}
这样springboot即可支持ueditor。
动静分离
tomact处理并发的能力远不如nginx。ueditor属于静态资源,应该部署到nginx。部署的过程这里就省略了,以后有空再补上。
参考资料:
https://blog.csdn.net/u013684276/article/details/80143343
http://fex.baidu.com/ueditor/