UEditor 不用过多赘述,百度旗下优秀的开源富文本编辑器,官网:http://ueditor.baidu.com/website/ 。
版本: UEditor 1.4.3 jsp版 ,应用服务器 tomcat。
背景:在使用UEditor的时候,用到了多图片上传和视频上传这两个组件('insertimage',"insertvideo"),如果不进行额外的配置,默认上传时会上传到项目的根目录下,其实这并不是一个好的做法,项目reload的时候,此前上传的图片和视频将会丢失。在自己写上传组件的时候,一般都会将图片上传到一个专门的磁盘路径下,而后再利用tomcat的虚拟路径映射配置,就能在项目中加载外部图片。查了下ue的配置项,并没有找到对应的虚拟路径配置的解决办法。于是发扬自己动手丰衣足食的精神,准备对源码下手。
ue的上传组件在ueditor-1.1.1.jar中,所以要先找到源码src,笔者用的是builder版本,所以源码直接在jsp的src文件夹下,建个java Application project,导入对应的src,目录结构如下:
对应的几个jar都是很常见的,笔者额外添加了一个commons-logging jar用来做日志的。
先说下笔者的修改思路(修改开源代码的原则是不破坏原有的结构),ue上传生成的文件名增加UUID策略,config.json 增加两个配置项,用于控制是否启用虚拟路径配置 和 虚拟路径映射的实际物理目录,下面对着代码讲:
先看下jsp/config.json的配置:
/* 上传图片配置项 */
"imageActionName": "uploadimage", /* 执行上传图片的action名称 */
"imageFieldName": "upfile", /* 提交的图片表单名称 */
"imageMaxSize": 2048000, /* 上传大小限制,单位B important */
"imageAllowFiles": [".png", ".jpg", ".jpeg", ".gif", ".bmp"], /* 上传图片格式显示 important */
"imageCompressEnable": true, /* 是否压缩图片,默认是true important */
"imageCompressBorder": 1600, /* 图片压缩最长边限制 important */
"imageInsertAlign": "none", /* 插入的图片浮动方式 */
"imageUrlPrefix": "http://192.168.0.177:8080/vsun", /* 图片访问路径前缀 TODO:上线部署时需要注意nginx环境对此配置项的影响 */
"imageUsingVirtualPath": "yes", /* 是否使用虚拟路径映射,设置为yes时开启 by will_awoke */
"imageRealMappingPath": "F:/dest/upload", /* 虚拟路径映射的实际物理目录,仅当imageUsingVirtualPath=yes时该配置有效 by will_awoke */
"imagePathFormat": "/media/image/{uuid}",
/* 禁用虚拟路径映射配置(imageUsingVirtualPath=no)但tomcat开启虚拟映射时,该配置项不能对应虚拟路径 */
/* "/ueditor/jsp/upload/image/{yyyy}{mm}{dd}/{time}{rand:6}",*/ /* 上传保存路径,可以自定义保存路径和文件名格式 */
/* {uuid} 推荐使用uuid by will_awoke */
/* 具请体看线上文档: fex.baidu.com/ueditor/#use-format_upload_filename */
/* 上传视频配置 */
"videoActionName": "uploadvideo", /* 执行上传视频的action名称 */
"videoFieldName": "upfile", /* 提交的视频表单名称 */
"videoMaxSize": 102400000, /* 上传大小限制,单位B,默认100MB */
"videoAllowFiles": [".mp4"], /* 上传视频格式显示 此处为了浏览器的兼容性,视频格式要求为mp4 by will_awoke*/
"videoUrlPrefix": "http://localhost:8083/vsun", /* 视频访问路径前缀 */
"videoUsingVirtualPath": "yes", /* 是否使用虚拟路径映射,设置为yes时开启 by will_awoke */
"videoRealMappingPath": "F:/dest/upload", /* 虚拟路径映射的实际物理目录,仅当imageUsingVirtualPath=yes时该配置有效 by will_awoke */
"videoPathFormat": "/media/video/{uuid}", /* 上传保存路径,可以自定义保存路径和文件名格式 */
/* 暂时只实现上传图片和视频支持虚拟映射,其他请参照实现(需修改ueditor.jar源码) */
以上传图片为例,这里增加了两个配置项:imageUsingVirtualPath 和 imageRealMappingPath,imageUsingVirtualPath 表示是否使用虚拟路径映射, 设置为no时表示关闭,设置为yes时开启;imageRealMappingPath 代表虚拟路径映射的实际物理目录,仅当imageUsingVirtualPath=yes时该配置有效。另外图片命名策略使用的是uuid。
uuid的策略增加到com.baidu.ueditor.PathFormat.java的getString方法
else if ( pattern.indexOf( PathFormat.UUID ) != -1 ) {
return UUIDGen.generate();
}
public static String generate()
{
UUID uuid = UUID.randomUUID();
return uuid.toString().replaceAll("-", "");
}
修改配置管理类 com.baidu.ueditor.ConfigManager.java 的getConfig方法
public Map<String, Object> getConfig ( int type ) {
Map<String, Object> conf = new HashMap<String, Object>();
String savePath = null;
//是否使用虚拟路径映射 默认不使用 by will_awoke
boolean virtualPath = false;
switch ( type ) {
/* 暂时只实现上传图片和视频支持虚拟映射,其他请参照实现(需修改ueditor.jar源码) */
case ActionMap.UPLOAD_FILE:
conf.put( "isBase64", "false" );
conf.put( "maxSize", this.jsonConfig.getLong( "fileMaxSize" ) );
conf.put( "allowFiles", this.getArray( "fileAllowFiles" ) );
conf.put( "fieldName", this.jsonConfig.getString( "fileFieldName" ) );
savePath = this.jsonConfig.getString( "filePathFormat" );
break;
case ActionMap.UPLOAD_IMAGE:
conf.put( "isBase64", "false" );
conf.put( "maxSize", this.jsonConfig.getLong( "imageMaxSize" ) );
conf.put( "allowFiles", this.getArray( "imageAllowFiles" ) );
conf.put( "fieldName", this.jsonConfig.getString( "imageFieldName" ) );
//for virtual path mapping
String imagePathFormat = this.jsonConfig.getString("imagePathFormat");
String imageUsingVirtualPath = this.jsonConfig.getString("imageUsingVirtualPath");
if("yes".equalsIgnoreCase(imageUsingVirtualPath)){
String imageRealMappingPath = this.jsonConfig.getString("imageRealMappingPath");
savePath = imageRealMappingPath + imagePathFormat;
virtualPath = true;
conf.put( "realMappingPath", imageRealMappingPath);//put into conf map using key=realMappingPath
log.debug("image real savePath(including PathFormat):" + savePath);
}else if("no".equalsIgnoreCase(imageUsingVirtualPath)){
log.debug("not virtual, using imagePathFormat as savePath");
savePath = imagePathFormat;
}else{
log.warn("invalid imageUsingVirtualPath in json.config:" + imageUsingVirtualPath+",it should be 'yes' or 'no'...");
}
//savePath = this.jsonConfig.getString( "imagePathFormat" );
break;
case ActionMap.UPLOAD_VIDEO:
conf.put( "maxSize", this.jsonConfig.getLong( "videoMaxSize" ) );
conf.put( "allowFiles", this.getArray( "videoAllowFiles" ) );
conf.put( "fieldName", this.jsonConfig.getString( "videoFieldName" ) );
//for virtual path mapping
String videoPathFormat = this.jsonConfig.getString("videoPathFormat");
String videoUsingVirtualPath = this.jsonConfig.getString("videoUsingVirtualPath");
if("yes".equalsIgnoreCase(videoUsingVirtualPath)){
String videoRealMappingPath = this.jsonConfig.getString("videoRealMappingPath");
savePath = videoRealMappingPath + videoPathFormat;
virtualPath = true;
conf.put( "realMappingPath", videoRealMappingPath);//put into conf map using key=realMappingPath
log.debug("video real savePath(including PathFormat):" + savePath);
}else if("no".equalsIgnoreCase(videoUsingVirtualPath)){
log.debug("not virtual, using videoPathFormat as savePath");
savePath = videoPathFormat;
}else{
log.warn("invalid videoUsingVirtualPath in json.config:" + videoUsingVirtualPath+",it should be 'yes' or 'no'...");
}
//savePath = this.jsonConfig.getString( "videoPathFormat" );
break;
//其他略....
}
conf.put( "savePath", savePath );
conf.put( "rootPath", this.rootPath );
conf.put( "virtualPath", virtualPath );//add put
return conf;
}
最后要修改上传类com.baidu.ueditor.upload.BinaryUploader save方法
public static final State save(HttpServletRequest request,
Map<String, Object> conf) {
FileItemStream fileStream = null;
boolean isAjaxUpload = request.getHeader( "X_Requested_With" ) != null;
if (!ServletFileUpload.isMultipartContent(request)) {
return new BaseState(false, AppInfo.NOT_MULTIPART_CONTENT);
}
ServletFileUpload upload = new ServletFileUpload(
new DiskFileItemFactory());
if ( isAjaxUpload ) {
upload.setHeaderEncoding( "UTF-8" );
}
try {
FileItemIterator iterator = upload.getItemIterator(request);
while (iterator.hasNext()) {
fileStream = iterator.next();
if (!fileStream.isFormField())
break;
fileStream = null;
}
if (fileStream == null) {
return new BaseState(false, AppInfo.NOTFOUND_UPLOAD_DATA);
}
String savePath = (String) conf.get("savePath");
String originFileName = fileStream.getName();
String suffix = FileType.getSuffixByFilename(originFileName);
originFileName = originFileName.substring(0,
originFileName.length() - suffix.length());
savePath = savePath + suffix;
long maxSize = ((Long) conf.get("maxSize")).longValue();
if (!validType(suffix, (String[]) conf.get("allowFiles"))) {
return new BaseState(false, AppInfo.NOT_ALLOW_FILE_TYPE);
}
//using PathFormat
savePath = PathFormat.parse(savePath, originFileName);
boolean virtualPath = (boolean)conf.get("virtualPath");
String physicalPath = (String) conf.get("rootPath") + savePath;
//启用虚拟路径时,不再使用 rootPath
if(virtualPath)
{
//此时savePath已含有实际的映射物理路径
physicalPath = savePath;//no rootPath
}
log.debug("file physicalPath:" + physicalPath);
InputStream is = fileStream.openStream();
State storageState = StorageManager.saveFileByInputStream(is,
physicalPath, maxSize);
is.close();
if (storageState.isSuccess()) {
storageState.putInfo("url", PathFormat.format(savePath));
//启动虚拟路径时,返回ue的url将不再携带realMappingPath,否则ue无法正常加载对应的已上传的图片/视频等
if(virtualPath){
String temp = (String) conf.get("realMappingPath");
storageState.putInfo("url", PathFormat.format(savePath.substring(temp.length())));
}
storageState.putInfo("type", suffix);
storageState.putInfo("original", originFileName + suffix);
}
return storageState;
} catch (FileUploadException e) {
log.error(e + " " + e.getMessage());
return new BaseState(false, AppInfo.PARSE_REQUEST_ERROR);
} catch (IOException e) {
log.error(e + " " + e.getMessage());
}
return new BaseState(false, AppInfo.IO_ERROR);
}
举个例子说明:
"imageUsingVirtualPath": "yes"
"imageRealMappingPath": "F:/dest/upload"
"imageUrlPrefix": "http://192.168.0.177:8080/vsun"
"imagePathFormat": "/media/image/{uuid}"
"videoUsingVirtualPath": "yes"
"videoRealMappingPath": "F:/dest/upload"
"videoUrlPrefix": "http://192.168.0.177:8080/vsun"
"videoPathFormat": "/media/video/{uuid}"
tomcat server.xml中配置的虚拟映射为 <Context docBase="F:/dest/upload/media" path="/vsun/media"/>,正确配置后,图片会上传到
f:/dest/upload/media/image/2f1ec7d9f441474aa47424c88899045b.jpg, 视频会上传到
f:/dest/upload/media/video/3658d1a3b2cb4a17a4907ba46854e9ca.mp4。 在ue中对应的链接分别是:
http://192.168.0.177:8080/vsun/media/image/2f1ec7d9f441474aa47424c88899045b.jpg, http://192.168.0.177:8080/vsun/media/video/3658d1a3b2cb4a17a4907ba46854e9ca.mp4,如果不出现404证明上述配置正确。
PS:这里提下几个简单的优化>
1. 多图上传只保留插入图片和本地上传,隐藏掉在线管理和图片搜索(注意修改的是html5 webuploader而非flash),修改dialogs/image/image.html,注释掉即可:
<!-- 在线管理
<span class="tab" data-content-id="online"><var id="lang_tab_online"></var></span>
-->
<!-- 图片搜索
<span class="tab" data-content-id="search"><var id="lang_tab_search"></var></span>
-->
2. 修改百度map组件的初始加载定位的是”江苏南京“,默认的是”北京“,首先修改lang/zh-cn下的zh-cn.js文件中的北京为江苏南京,
'map':{
'static':{
lang_city:"城市",
lang_address:"地址",
city:{value:"江苏南京"},
lang_search:"搜索",
lang_dynamicmap:"插入动态地图"
},
cityMsg:"请选择城市",
errorMsg:"抱歉,找不到该位置!"
}
然后修改dialogs/map/map.html,对应经纬度即可。
point = new BMap.Point(118.78, 32.04); // 创建“江苏南京”点坐标
//point = new BMap.Point(116.404, 39.915); // 创建点坐标 (原坐标为北京)
笔者自己定制的ueditor上传组件,下载地址: http://download.csdn.net/detail/liuyuhua0066/7978873
项目git :https://github.com/fex-team/ueditor