那么开始吧!
这是我的项目目录
1.从访问路径http://localhost:8081/Test/_examples/simpleDemo.html,我们主要是要看看,富文本框被加载出来之前,会调用哪些代码,
不卖关子,很明显,会调用后端的controller.jsp代码,因为我们已经在ueditor.config配置了:
1
|
, serverUrl: URL + "jsp/controller.js |
看看controller.jsp代码,上一篇文章我们已经讲了,要把这些代码看作是后端代码,很重要很重要的:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
<%@ page language= "java" contentType= "text/html; charset=UTF-8" import = "com.baidu.ueditor.ActionEnter"
pageEncoding= "UTF-8" %>
<%@ page trimDirectiveWhitespaces= "true" %>
<% request.setCharacterEncoding( "utf-8" );
response.setHeader( "Content-Type" , "text/html" );
/** 项目根路径 **/
String rootPath = application.getRealPath( "/" );
/** 调用后端的ActionEnter类,并执行exec方法 **/
out.write( new ActionEnter( request, rootPath ).exec() );
%> |
我们就到ActionEnter.java看看吧,这个类就是前端调用后端的唯一入口,也只有这个入口了,记住第一章有讲了,要把源码复制到src下,进行调试哦!不知道先看第一章吧!!!!!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
package com.baidu.ueditor;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import com.baidu.ueditor.define.ActionMap;
import com.baidu.ueditor.define.AppInfo;
import com.baidu.ueditor.define.BaseState;
import com.baidu.ueditor.define.State;
import com.baidu.ueditor.hunter.FileManager;
import com.baidu.ueditor.hunter.ImageHunter;
import com.baidu.ueditor.upload.Uploader;
public class ActionEnter {
private HttpServletRequest request = null ;
private String rootPath = null ;
private String contextPath = null ;
private String actionType = null ;
private ConfigManager configManager = null ;
/** action统一入口 **/ public ActionEnter ( HttpServletRequest request, String rootPath ) {
this .request = request;
/** rootPath = /Test/ **/
this .rootPath = rootPath;
/** actionType = config **/
this .actionType = request.getParameter( "action" );
/** contextPath = /Test **/
this .contextPath = request.getContextPath();
/** 调用ConfigManager **/
this .configManager = ConfigManager.getInstance( this .rootPath, this .contextPath, request.getRequestURI() );
} |
2.ConfigManager类主要用来读取后端的配置文件,就是config.json这个文件,事实上这个文件应该放在后端的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
|
* 配置管理器 * @author hancong03 @baidu .com
*
*/
public final class ConfigManager {
private final String rootPath;
private final String originalPath;
private final String contextPath;
/** 存放备注文件 **/ private static final String configFileName = "config.json" ;
private String parentPath = null ;
private JSONObject jsonConfig = null ;
// 涂鸦上传filename定义 private final static String SCRAWL_FILE_NAME = "scrawl" ;
// 远程图片抓取filename定义 private final static String REMOTE_FILE_NAME = "remote" ;
/* * 通过一个给定的路径构建一个配置管理器, 该管理器要求地址路径所在目录下必须存在config.properties文件
*/
private ConfigManager ( String rootPath, String contextPath, String uri ) throws FileNotFoundException, IOException { rootPath = rootPath.replace( "\\", "/" );
//下面的rootPath就是我的根路径
// rootPath=D:/workspace_de_client/.metadata/.plugins/org.eclipse.wst.server.core/tmp0/wtpwebapps/Test/
this.rootPath = rootPath;
this.contextPath = contextPath;
//请求路径 url = /Test/jsp/controller.jsp
if ( contextPath.length() > 0 ) {
// D:/workspace_de_client/.metadata/.plugins/org.eclipse.wst.server.core/tmp0/wtpwebapps/Test//jsp/controller.jsp
this.originalPath = this.rootPath + uri.substring( contextPath.length() );
} else {
this.originalPath = this.rootPath + uri;
}
/** 调用当前类的初始化环境方法 initEnv **/
this.initEnv();
} //上面的方法无法就是获得controller.jsp这个类所在的真实目录而已 //下面看看initEnv()这个方法 private void initEnv () throws FileNotFoundException, IOException { /** **/
File file = new File( this.originalPath );
if ( !file.isAbsolute() ) {
file = new File( file.getAbsolutePath() );
}
/** 获得文件的父路径,也就是 ..../jsp **/
this.parentPath = file.getParent();
/** 读取配置文件,这个方法比较重要,往下看 **/
String configContent = this.readFile( this.getConfigPath() );
try{
/** 把返回的的json字符串扔进JsonObject对象中 **/
JSONObject jsonConfig = new JSONObject( configContent );
this.jsonConfig = jsonConfig;
} catch ( Exception e ) {
this.jsonConfig = null;
}
} /** 获得配置路径,记住config.json是和controller.jsp放在同一个目录下的,很坑有木有 **/ private String getConfigPath () { /** 拼凑config.json的真实路径 **/
return this.parentPath + File.separator + ConfigManager.configFileName;
} private String[] getArray ( String key ) { JSONArray jsonArray = this.jsonConfig.getJSONArray( key );
String[] result = new String[ jsonArray.length() ];
for ( int i = 0, len = jsonArray.length(); i < len; i++ ) {
result[i] = jsonArray.getString( i );
}
return result;
} /** 获得配置文件的内容,变成字符串返回 **/ private String readFile ( String path ) throws IOException { StringBuilder builder = new StringBuilder();
try {
InputStreamReader reader = new InputStreamReader( new FileInputStream( path ), "UTF-8" );
BufferedReader bfReader = new BufferedReader( reader );
String tmpContent = null;
while ( ( tmpContent = bfReader.readLine() ) != null ) {
builder.append( tmpContent );
}
bfReader.close();
} catch ( UnsupportedEncodingException e ) {
// 忽略
}
//过滤输入字符串, 剔除多行注释以及替换掉反斜杠
return this.filter( builder.toString() );
} // 过滤输入字符串, 剔除多行注释以及替换掉反斜杠 private String filter ( String input ) { return input.replaceAll( "/\\*[\\s\\S]*?\\*/ ", "" );
} |
从上面的方法中,读取配置文件的所有后端代码就都执行完了吧!!!!很简单吧!!!!
3.后端代码执行完之后,富文本框就初始化出来了,很有成就感吧!!
如图:
我们点击上传图片的按钮,选择一张图片上传,好,接下来就看看前端是如何调用,以及后端是如何保存文件的吧!!
我们看simpleupload.js这个文件,它是实现单文本上传的主要前段代码,很重要的
我们从53行开始看也就是:domUtils.on(input, ‘change’, function() 找不到可以Ctrl +F,相信这点技能还是有的吧,否则就不适合这个行业了 !!!!
这里我要说明下,ueditor读取配置文件的顺序,是:
1
|
后端返回json配置文件 --> 用户自定义的配置文件 --> ueditor.config |
好了,开始来调用后端的代码了,一样是调用ActionEnter.java这个类
前端的请求路径是
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
"http://localhost:8081/Test/jsp/controller.jsp?action=uploadimage" /** * 处理不同类型的回调函数
* @return
*/
public String invoke() {
//自己添加上去的
Map<string, integer= "" > mapping = ActionMap.mapping;
if ( actionType == null || !ActionMap.mapping.containsKey( actionType ) ) {
return new BaseState( false , AppInfo.INVALID_ACTION ).toJSONString();
}
if ( this .configManager == null || ! this .configManager.valid() ) {
return new BaseState( false , AppInfo.CONFIG_ERROR ).toJSONString();
}
State state = null ;
// 获得actionType类型码 ActionMap这个类我就不介绍了,自己可以看看,主要是封装的一些常量
int actionCode = ActionMap.getType( this .actionType );
Map<string, object= "" > conf = null ;
switch ( actionCode ) {
//如果配置文件,执行下面的方法,这个就是我们开始讲的后端读取的配置文件执行的逻辑
case ActionMap.CONFIG:
return this .configManager.getAllConfig().toString();
//这里是我们这次重点讲解的路径,图片上传,视频上传都执行这个路基
case ActionMap.UPLOAD_IMAGE:
case ActionMap.UPLOAD_SCRAWL:
case ActionMap.UPLOAD_VIDEO:
case ActionMap.UPLOAD_FILE:
//发现没有,这里获得配置文件,看到这里,你应该先看getConfig这个方法,跟着思路走,不是是跟着调试代码走
conf = this .configManager.getConfig( actionCode );
//这里就是执行文件上传的方法了,看完上面代码才可以看这里呀,不要急
state = new Uploader( request, conf ).doExec();
break ;
case ActionMap.CATCH_IMAGE:
conf = configManager.getConfig( actionCode );
String[] list = this .request.getParameterValues( (String)conf.get( "fieldName" ) );
state = new ImageHunter( conf ).capture( list );
break ;
case ActionMap.LIST_IMAGE:
case ActionMap.LIST_FILE:
conf = configManager.getConfig( actionCode );
int start = this .getStartIndex();
state = new FileManager( conf ).listFile( start );
break ;
}
return state.toJSONString();
} |
看看configManager.getConfig这个类
//如何是获得后端的所有配置,调用这个方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
|
public JSONObject getAllConfig () {
return this .jsonConfig;}
//获得部分的配置 public Map<string, object= "" > getConfig ( int type ) {
Map<string, object= "" > conf = new HashMap<string, object= "" >();
String savePath = null ;
switch ( type ) {
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" ) );
//看看看,走在路上别瞎看,看这里很重要的
//imageFieldName 图片名称
conf.put( "fieldName" , this .jsonConfig.getString( "imageFieldName" ) );
//图片保存路径,有没有发现在config.json配置的imagePathFormat返回前端变成savePath
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" ) );
savePath = this .jsonConfig.getString( "videoPathFormat" );
break ;
case ActionMap.UPLOAD_SCRAWL:
conf.put( "filename" , ConfigManager.SCRAWL_FILE_NAME );
conf.put( "maxSize" , this .jsonConfig.getLong( "scrawlMaxSize" ) );
conf.put( "fieldName" , this .jsonConfig.getString( "scrawlFieldName" ) );
conf.put( "isBase64" , "true" );
savePath = this .jsonConfig.getString( "scrawlPathFormat" );
break ;
case ActionMap.CATCH_IMAGE:
conf.put( "filename" , ConfigManager.REMOTE_FILE_NAME );
conf.put( "filter" , this .getArray( "catcherLocalDomain" ) );
conf.put( "maxSize" , this .jsonConfig.getLong( "catcherMaxSize" ) );
conf.put( "allowFiles" , this .getArray( "catcherAllowFiles" ) );
conf.put( "fieldName" , this .jsonConfig.getString( "catcherFieldName" ) + "[]" );
savePath = this .jsonConfig.getString( "catcherPathFormat" );
break ;
case ActionMap.LIST_IMAGE:
conf.put( "allowFiles" , this .getArray( "imageManagerAllowFiles" ) );
conf.put( "dir" , this .jsonConfig.getString( "imageManagerListPath" ) );
conf.put( "count" , this .jsonConfig.getInt( "imageManagerListSize" ) );
break ;
case ActionMap.LIST_FILE:
conf.put( "allowFiles" , this .getArray( "fileManagerAllowFiles" ) );
conf.put( "dir" , this .jsonConfig.getString( "fileManagerListPath" ) );
conf.put( "count" , this .jsonConfig.getInt( "fileManagerListSize" ) );
break ;
}
conf.put( "savePath" , savePath );
conf.put( "rootPath" , this .rootPath );
return conf;
} |
接下了这个类Uploader.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
package com.baidu.ueditor.upload;
import com.baidu.ueditor.define.State;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
public class Uploader {
private HttpServletRequest request = null ;
private Map<string, object= "" > conf = null ;
public Uploader(HttpServletRequest request, Map<string, object= "" > conf) {
this .request = request;
this .conf = conf;
}
public final State doExec() {
String filedName = (String) this .conf.get( "fieldName" );
State state = null ;
//重点在这里
if ( "true" .equals( this .conf.get( "isBase64" ))) {
//重点看这里,好了,知道我们要干嘛了吧!!!看Base64Uploader类的代码
state = Base64Uploader.save( this .request.getParameter(filedName),
this .conf);
} else {
state = BinaryUploader.save( this .request, this .conf);
}
return state;
}
} |
接下来看这个类的方法:BaseUploader.java,这里的save方法就是把文件保存到硬盘上
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
|
package com.baidu.ueditor.upload;
import com.baidu.ueditor.PathFormat;
import com.baidu.ueditor.define.AppInfo;
import com.baidu.ueditor.define.BaseState;
import com.baidu.ueditor.define.FileType;
import com.baidu.ueditor.define.State;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.fileupload.FileItemIterator;
import org.apache.commons.fileupload.FileItemStream;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
public class BinaryUploader {
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);
}
//common-io包中类,用于文件上传
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 = (String) conf.get( "rootPath" ) + savePath;
InputStream is = fileStream.openStream();
//这里就是把文件保存到硬盘上,具体怎么保存的可以自己跟过去看看
//State这个类很重要,是一个接口,它是返回到前端的数据
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) {
return new BaseState( false , AppInfo.PARSE_REQUEST_ERROR);
} catch (IOException e) {
}
return new BaseState( false , AppInfo.IO_ERROR);
}
private static boolean validType(String type, String[] allowTypes) {
List<string> list = Arrays.asList(allowTypes);
return list.contains(type);
}
} |
最后,我们再看一个类就是State这个类,它是一个接口,我们主要是看它的实现类BaseState
这个类很重要,很重要,很重要,重要事情说3遍:::
介绍下吧:
这个类主要是返回前端的数据,格式就是下面这个样子,格式一定要对,否则前端会出现问题
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
|
package com.baidu.ueditor.define;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import com.baidu.ueditor.Encoder;
public class BaseState implements State {
//状态码
private boolean state = false ;
private String info = null ;
//里面存保存好的文件路径和
private Map<string, string= "" > infoMap = new HashMap<string, string= "" >();
public BaseState () {
this .state = true ;
}
public BaseState ( boolean state ) {
this .setState( state );
}
public BaseState ( boolean state, String info ) {
this .setState( state );
this .info = info;
}
public BaseState ( boolean state, int infoCode ) {
this .setState( state );
this .info = AppInfo.getStateInfo( infoCode );
}
public boolean isSuccess () {
return this .state;
}
public void setState ( boolean state ) {
this .state = state;
}
public void setInfo ( String info ) {
this .info = info;
}
public void setInfo ( int infoCode ) {
this .info = AppInfo.getStateInfo( infoCode );
}
@Override
public String toJSONString() {
return this .toString();
}
/** 这里很重要的,也很简单,它把infoMap手工拼凑成json字符串返回回去 **/
public String toString () {
String key = null ;
String stateVal = this .isSuccess() ? AppInfo.getStateInfo( AppInfo.SUCCESS ) : this .info;
StringBuilder builder = new StringBuilder();
builder.append( "{\"state\": \"" + stateVal + "\"" );
Iterator<string> iterator = this .infoMap.keySet().iterator();
while ( iterator.hasNext() ) {
key = iterator.next();
builder.append( ",\"" + key + "\": \"" + this .infoMap.get(key) + "\"" );
}
builder.append( "}" );
return Encoder.toUnicode( builder.toString() );
}
@Override
public void putInfo(String name, String val) {
this .infoMap.put(name, val);
}
@Override
public void putInfo(String name, long val) {
this .putInfo(name, val+ "" );
}
} |
4.上面后端的代码已经调完了,接下来就是后端数据返回到前端
之前讲过,看前端的simpleupload.js
后端返回数据后会调用回调函数的callback()方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
function callback(){ try {
var link, json, loader,
body = (iframe.contentDocument || iframe.contentWindow.document).body,
result = body.innerText || body.textContent || '' ;
//这里result就是后端返回的数据
json = ( new Function( "return " + result))();
//imageUrlPrefix这个很重要很重要很重要,如果没配置的话,图片可能显示不出来
//link就是图片的路径
link = me.options.imageUrlPrefix + json.url;
if (json.state == 'SUCCESS' && json.url) {
loader = me.document.getElementById(loadingId);
loader.setAttribute( 'src' , link);
loader.setAttribute( '_src' , link);
loader.setAttribute( 'title' , json.title || '' );
loader.setAttribute( 'alt' , json.original || '' );
loader.removeAttribute( 'id' );
domUtils.removeClasses(loader, 'loadingclass' );
} else {
showErrorLoader && showErrorLoader(json.state);
}
} catch (er){
showErrorLoader && showErrorLoader(me.getLang( 'simpleupload.loadError' ));
}
form.reset();
domUtils.un(iframe, 'load' , callback);
}
function showErrorLoader(title){
if (loadingId) {
var loader = me.document.getElementById(loadingId);
loader && domUtils.remove(loader);
me.fireEvent( 'showmessage' , {
'id' : loadingId,
'content' : title,
'type' : 'error' ,
'timeout' : 4000
});
}
}
|
参考:http://blog.csdn.net/huangwenyi1010/article/details/51637439