Ueditor的图片上传默认是上传到项目下的,而项目所处目录是通过controller.jsp中的application.getRealPath( "/" )传进去的。可惜的是这个目录不能改,改了之后会出现一个很常见的问题——后端配置项没有正常加载,上传插件不能正常使用,因为需要利用这个目录查找配置文件及其他一些文件的路径。
所以要想将图片上传到项目外,只能修改源码。
改的思路有很多种,我这里用的是在config.json文件中添加一个属性,然后后台读取,具体步骤和代码如下:
1、在config.json文件的末尾添加属性:
"outsideSavePath":"D:/bfp"/*项目外部链接,默认情况下是在项目底下,配置该字段之后可以配置在项目外面*/
这里有两个注意点:
a、在上一个属性的末尾添加逗号
b、添加注释时一定要用/**/的形式,使用别的注释形式如//会报错,错误也是经典的后端配置项没有正常加载,上传插件不能正常使用
2、在ConfigManager.java的getConfig ( int type ) 方法中添加对该属性的读取:
conf.put("outsideSavePath",this.jsonConfig.getString( "outsideSavePath"));//项目外部路径
conf.put( "savePath", savePath );
conf.put( "rootPath", this.rootPath );
return conf;
我这里存的是outsideSavePath
3、在图片上传的BinaryUploader.java的save()方法中读取该属性:
String physicalPath="";
//若有指定项目外配置路径则使用外配路径,否则使用项目内路径
if(conf.get("outsideSavePath")!=null && conf.get("outsideSavePath").toString().trim().length()>0){
physicalPath = (String) conf.get("outsideSavePath") + savePath;
savePath = physicalPath;//将绝对物理路径返回
}else{
//项目内的,将相对物理路径返回
physicalPath = (String) conf.get("rootPath") + savePath;
}
该方法的完整代码如下:
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);
}
savePath = PathFormat.parse(savePath, originFileName);
String physicalPath="";
//若有指定项目外配置路径则使用外配路径,否则使用项目内路径
if(conf.get("outsideSavePath")!=null && conf.get("outsideSavePath").toString().trim().length()>0){
physicalPath = (String) conf.get("outsideSavePath") + savePath;
savePath = physicalPath;//将绝对物理路径返回
}else{
//项目内的,将相对物理路径返回
physicalPath = (String) conf.get("rootPath") + savePath;
}
//System.out.println("文件上传路径:"+physicalPath);
InputStream is = fileStream.openStream();
State storageState = StorageManager.saveFileByInputStream(is,physicalPath, maxSize);
is.close();
if (storageState.isSuccess()) {
storageState.putInfo("url", PathFormat.format(savePath));
storageState.putInfo("type", suffix);
storageState.putInfo("original", originFileName + suffix);
}
return storageState;
} catch (FileUploadException e) {
e.printStackTrace();
return new BaseState(false, AppInfo.PARSE_REQUEST_ERROR);
} catch (IOException e) {
e.printStackTrace();
}
return new BaseState(false, AppInfo.IO_ERROR);
}
源码中除了BinaryUploader.java可以上传图片/文件之后,Base64Uploader.java也能实现该功能,至于两个方法什么时候用哪个没有做研究,所以我在这两个方法中都添加了这段代码。当然图片上传用的是BinaryUploader.java。
修改后的源码及源码打成的jar已经上传到附件,有需要的可以下载。
(PS:最初的源码里面所有的catch都没有printStackTrace(),因此无论系统报什么错都不会有任何提示。我是这上面吃过不好亏,所以在源码的每个catch中都添加了printStackTrace(),并且打到jar包里了。介意的勿下载)
4、修改页面的显示方式:
按照以前的思路:返回的是项目底下的路径,img标签的src直接使用路径就能显示图片了。但是这个改成服务器上的绝对物理路径了,还用以前的路径绝对显示不出来。显示图片的方式有很多,我这里使用项目外挂的方式。具体外挂步骤参考javaweb使用虚拟目录显示非项目文件下的图片信息
图片上传完成之后会进入到ueditor.all.js中的function unhtmlData(imgCi) {方法,它的具体流程我没有过多的研究,只是在html代码拼接的时候进行了拦截,修改代码如下:
var img_src = ci.src;
var widthStyle = "";
if(img_src.indexOf("http://")!=-1 || img_src.indexOf("https://")!=-1){//表情符号
var width = getImgWidth(img_src);
widthStyle="style=\"width:"+width+"px;height:"+width+"px;\"";
}else{
img_src = formatImgSrc(img_src);
}
str = '<img '+widthStyle+' src="' + img_src + '" ' + (ci._src ? ' _src="' + ci._src + '" ' : '') 。。。。
具体代码如下:
if (img && /img/i.test(img.tagName) && (img.className != "edui-faked-video" || img.className.indexOf("edui-upload-video")!=-1) && !img.getAttribute("word_img")) { var first = opt.shift(); var floatStyle = first['floatStyle']; delete first['floatStyle']; domUtils.setAttributes(img, first); me.execCommand('imagefloat', floatStyle); if (opt.length > 0) { range.setStartAfter(img).setCursor(false, true); me.execCommand('insertimage', opt); } } else { var html = [], str = '', ci; ci = opt[0]; if (opt.length == 1) { unhtmlData(ci); var img_src = ci.src; var widthStyle = ""; if(img_src.indexOf("http://")!=-1 || img_src.indexOf("https://")!=-1){//表情符号 var width = getImgWidth(img_src); widthStyle="style=\"width:"+width+"px;height:"+width+"px;\""; }else{ img_src = formatImgSrc(img_src); } str = '<img '+widthStyle+' src="' + img_src + '" ' + (ci._src ? ' _src="' + ci._src + '" ' : '') + (ci.width ? 'width="' + ci.width + '" ' : '') + (ci.height ? ' height="' + ci.height + '" ' : '') + (ci['floatStyle'] == 'left' || ci['floatStyle'] == 'right' ? ' style="float:' + ci['floatStyle'] + ';"' : '') + (ci.title && ci.title != "" ? ' title="' + ci.title + '"' : '') + (ci.border && ci.border != "0" ? ' border="' + ci.border + '"' : '') + (ci.alt && ci.alt != "" ? ' alt="' + ci.alt + '"' : '') + (ci.hspace && ci.hspace != "0" ? ' hspace = "' + ci.hspace + '"' : '') + (ci.vspace && ci.vspace != "0" ? ' vspace = "' + ci.vspace + '"' : '') + '/>'; if (ci['floatStyle'] == 'center') { str = '<p style="text-align: center">' + str + '</p>'; } html.push(str); } else { for (var i = 0; ci = opt[i++];) { unhtmlData(ci); var img_src = formatImgSrc(ci.src); str = '<p ' + (ci['floatStyle'] == 'center' ? 'style="text-align: center" ' : '') + '><img src="' + img_src + '" ' + (ci.width ? 'width="' + ci.width + '" ' : '') + (ci._src ? ' _src="' + ci._src + '" ' : '') + (ci.height ? ' height="' + ci.height + '" ' : '') + ' style="' + (ci['floatStyle'] && ci['floatStyle'] != 'center' ? 'float:' + ci['floatStyle'] + ';' : '') + (ci.border || '') + '" ' + (ci.title ? ' title="' + ci.title + '"' : '') + ' /></p>'; html.push(str); } } me.execCommand('insertHtml', html.join('')); }
PS:for的那段代码在多图片上传时有用,单图片上传或使用表情都是的上一段代码
用到的两个js方法如下:
//获取url基本信息 function getURLInfo(){ var projectName="";//项目名称 projectName = window.location.pathname;// /XXX/XXXX!XXX.action projectName = projectName.substring(1,(projectName.substring(1).indexOf("/")+1));//获取第二个/所在的地点 //协议:http //主机 127.0.0.1:8090 host_URL = window.location.protocol+"//"+window.location.host; project_URL = host_URL+"/"+projectName; } //根据图片或文件保存在项目底下或者非项目底下判断图片的显示方式 function formatImgSrc(img_src){ if(img_src.indexOf(":")!=-1){//说明用的项目外目录,那么使用外挂的形式访问图片 img_src = host_URL+img_src.substring(2); }else{//使用普通方式 img_src=project_URL+img_src; } return img_src; }
主要功能是为img的src添加项目信息,变成类似于:http://192.168.0.100:8080/test/ueditor/20170531/img_12346799.jpg或者:http://192.168.0.100:8080/bfp/ueditor/20170531/img_12346799.jpg的目录。
注:这个目录会保存到数据库中,页面在访问的时候也会显示上面的具体路径。
最后还有一个辅助方法,具体用途跟下一篇要讲的问题有关,这里先贴一下代码,保证完整性:
/** * 该方法根据表情包所在文件夹分类返回图片的粗略大小以解决表情在手机上出现拉伸的问题 * **/ function getImgWidth(img_src){ var width = 0; var bbb = img_src.substr(img_src.lastIndexOf('/', img_src.lastIndexOf('/') - 1) + 1); var emotion = bbb.substr(0,bbb.indexOf("/"));//表情包文件夹 switch(emotion){ case "babycat"://baby猫 case "bobo"://BOBO(大部分是50x50,少数为其他大小) case "ldw"://绿豆蛙 case "tsj"://兔斯基 width=50;break case "face"://泡泡 表情 width=25;break; case "youa"://有啊 width=60;break; case "jx2"://推荐 var ddd = bbb.substr(bbb.indexOf("/")+3,4);//名称序号 ddd = parseInt(ddd); //兔斯基、绿豆蛙、bobo、baby猫 if(ddd<=56){width=50; }else if(ddd>=71){//有啊 width=60; }else{//泡泡表情 width=25; } break; } return width; }
至此,“将图片上传至项目外的目录”完成,希望对大家有所帮助。大家加油!