之前写过一版本的ueditor的使用方式,感觉后来ueditor升级很快,转眼间又升级了,今天有一个人问这块相关的问题,正好又熟悉下。
首先最基本的用法我就不讲了,只讲文件上传的这块。
首先,文件上传这块和之前的变化很大,先慢慢的讲讲用法。
1. java版本的在jsp目录的文件结构如下:
从这地方可以看出来,有一个controller.jsp, 一个config.json,一堆jar文件, 这个和之前版本是不一致的。
2. maven工程的jar包的引用
如果没有使用jar包,很容易,直接copy文件就可以,但是maven的方式,这个jar又在网上没有,索幸maven提供了system方式的依赖方式:
Xml代码
< dependency> < groupId> com.baidu.ueditor </groupId > < artifactId> ueditor </artifactId > < version> 1.1.1 </version > < scope> system </scope > < systemPath> ${basedir}/ src/main/webapp /WEB-INF/lib/ ueditor-1.1.1.jar </systemPath > </ dependency>
maven的jar包的放置位置如下:
其他的jar我就不多讲了,都很容易找。
3. controller.jsp文件阅读
Html代码
<%@ 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( "/" ); out.write( new ActionEnter( request, rootPath ).exec() ); %>
从代码来看,rootPath其实就是项目的根路径,构建了ActionEnter,并调用了exec函数。
我们来看下ActionEnter的代码:
Java代码
package com.baidu.ueditor; import com.baidu.ueditor.define.ActionMap; 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; import java.util.Map; import javax.servlet.http.HttpServletRequest; import org.json.JSONObject; public class ActionEnter { private HttpServletRequest request = null; private String rootPath = null; private String contextPath = null; private String actionType = null; private ConfigManager configManager = null; public ActionEnter(HttpServletRequest request, String rootPath) { this.request = request; this.rootPath = rootPath; this.actionType = request.getParameter("action"); this.contextPath = request.getContextPath(); this.configManager = ConfigManager.getInstance(this.rootPath, this.contextPath, request.getRequestURI()); } public String exec() { String callbackName = this.request.getParameter("callback"); if (callbackName != null) { if (!validCallbackName(callbackName)) { return new BaseState(false, 401).toJSONString(); } return callbackName + "(" + invoke() + ");"; } return invoke(); } public String invoke() { if ((this.actionType == null) || (!ActionMap.mapping.containsKey(this.actionType))) { return new BaseState(false, 101).toJSONString(); } if ((this.configManager == null) || (!this.configManager.valid())) { return new BaseState(false, 102).toJSONString(); } State state = null; int actionCode = ActionMap.getType(this.actionType); Map conf = null; switch (actionCode) { case 0: return this.configManager.getAllConfig().toString(); case 1: case 2: case 3: case 4: conf = this.configManager.getConfig(actionCode); state = new Uploader(this.request, conf).doExec(); break; case 5: conf = this.configManager.getConfig(actionCode); String[] list = this.request.getParameterValues((String)conf.get("fieldName")); state = new ImageHunter(conf).capture(list); break; case 6: case 7: conf = this.configManager.getConfig(actionCode); int start = getStartIndex(); state = new FileManager(conf).listFile(start); } return state.toJSONString(); } public int getStartIndex() { String start = this.request.getParameter("start"); try { return Integer.parseInt(start); } catch (Exception e) { } return 0; } public boolean validCallbackName(String name) { if (name.matches("^[a-zA-Z_]+[\\w0-9_]*$")) { return true; } return false; } }
我们慢慢的来看这个函数:首先在构造函数里面调用了request.getContextPath()和request.getRequestURI()函数。
假设我们的项目的contextPath为:test,那么下面两个函数的返回值则如下:
request.getContextPath /test
request.getRequestURI /test/resources/ueditor/jsp/controller.jsp
我们还是先来看下ConfigManager类吧。
Java代码
package com.baidu.ueditor; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.util.HashMap; import java.util.Map; import org.json.JSONArray; import org.json.JSONObject; 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; private static final String SCRAWL_FILE_NAME = "scrawl"; private static final String REMOTE_FILE_NAME = "remote"; private ConfigManager(String rootPath, String contextPath, String uri) throws FileNotFoundException, IOException { rootPath = rootPath.replace("\\", "/"); this.rootPath = rootPath; this.contextPath = contextPath; if (contextPath.length() > 0) this.originalPath = (this.rootPath + uri.substring(contextPath.length())); else { this.originalPath = (this.rootPath + uri); } initEnv(); } public static ConfigManager getInstance(String rootPath, String contextPath, String uri) { try { return new ConfigManager(rootPath, contextPath, uri); } catch (Exception e) { } return null; } public boolean valid() { return this.jsonConfig != null; } public JSONObject getAllConfig() { return this.jsonConfig; } public Map<String, Object> getConfig(int type) { Map conf = new HashMap(); String savePath = null; switch (type) { case 4: conf.put("isBase64", "false"); conf.put("maxSize", Long.valueOf(this.jsonConfig.getLong("fileMaxSize"))); conf.put("allowFiles", getArray("fileAllowFiles")); conf.put("fieldName", this.jsonConfig.getString("fileFieldName")); savePath = this.jsonConfig.getString("filePathFormat"); break; case 1: conf.put("isBase64", "false"); conf.put("maxSize", Long.valueOf(this.jsonConfig.getLong("imageMaxSize"))); conf.put("allowFiles", getArray("imageAllowFiles")); conf.put("fieldName", this.jsonConfig.getString("imageFieldName")); savePath = this.jsonConfig.getString("imagePathFormat"); break; case 3: conf.put("maxSize", Long.valueOf(this.jsonConfig.getLong("videoMaxSize"))); conf.put("allowFiles", getArray("videoAllowFiles")); conf.put("fieldName", this.jsonConfig.getString("videoFieldName")); savePath = this.jsonConfig.getString("videoPathFormat"); break; case 2: conf.put("filename", "scrawl"); conf.put("maxSize", Long.valueOf(this.jsonConfig.getLong("scrawlMaxSize"))); conf.put("fieldName", this.jsonConfig.getString("scrawlFieldName")); conf.put("isBase64", "true"); savePath = this.jsonConfig.getString("scrawlPathFormat"); break; case 5: conf.put("filename", "remote"); conf.put("filter", getArray("catcherLocalDomain")); conf.put("maxSize", Long.valueOf(this.jsonConfig.getLong("catcherMaxSize"))); conf.put("allowFiles", getArray("catcherAllowFiles")); conf.put("fieldName", this.jsonConfig.getString("catcherFieldName") + "[]"); savePath = this.jsonConfig.getString("catcherPathFormat"); break; case 7: conf.put("allowFiles", getArray("imageManagerAllowFiles")); conf.put("dir", this.jsonConfig.getString("imageManagerListPath")); conf.put("count", Integer.valueOf(this.jsonConfig.getInt("imageManagerListSize"))); break; case 6: conf.put("allowFiles", getArray("fileManagerAllowFiles")); conf.put("dir", this.jsonConfig.getString("fileManagerListPath")); conf.put("count", Integer.valueOf(this.jsonConfig.getInt("fileManagerListSize"))); } conf.put("savePath", savePath); conf.put("rootPath", this.rootPath); return conf; } private void initEnv() throws FileNotFoundException, IOException { File file = new File(this.originalPath); if (!file.isAbsolute()) { file = new File(file.getAbsolutePath()); } this.parentPath = file.getParent(); String configContent = readFile(getConfigPath()); try { JSONObject jsonConfig = new JSONObject(configContent); this.jsonConfig = jsonConfig; } catch (Exception e) { this.jsonConfig = null; } } private String getConfigPath() { return this.parentPath + File.separator + "config.json"; } private String[] getArray(String key) { JSONArray jsonArray = this.jsonConfig.getJSONArray(key); String[] result = new String[jsonArray.length()]; int i = 0; for (int 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 localUnsupportedEncodingException) { } return filter(builder.toString()); } private String filter(String input) { return input.replaceAll("/\\*[\\s\\S]*?\\*/", ""); } }
我们再来看
Uploader函数,其实很简单:
Java代码
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"))) state = Base64Uploader.save(this.request.getParameter(filedName), this.conf); else { state = BinaryUploader.save(this.request, this.conf); } return state; } }
这个很好理解,我们接着来看BinaryUploader类:
Java代码
package com.baidu.ueditor.upload; import com.baidu.ueditor.PathFormat; 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, 5); } 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, 7); } 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, 8); } savePath = PathFormat.parse(savePath, originFileName); String physicalPath = (String)conf.get("rootPath") + savePath; 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) { return new BaseState(false, 6); } catch (IOException localIOException) { } return new BaseState(false, 4); } private static boolean validType(String type, String[] allowTypes) { List list = Arrays.asList(allowTypes); return list.contains(type); } }
StorageManager我们就不看了,无非就是做一些文件存储的一些事情,下面我们来分析下这种实现方式的问题。
最后我稍微总结下看这个代码得收获和对作者的建议:
从这个地方来看,无法将图片放置在外部路径,因为这种实现就决定了只能放到项目路径下,
这个最大的问题就是,有可能不小心,重新上下线,内容全部丢了。 从实现来看,大量的使用静态调用,基本上无法二次开发,不能灵活的继承它来处理个性化的东西,
比如如果存储到fastDFS里面,这个就需要改里面的代码,不能通过扩展的方式来进行 config.json里面的配置项转换的时候,进行了重命名,这个地方就要求读者要记两个变量名,比如:imagePathFormat变成了savePath, 感觉好像挺好理解,但是这种明显不是好的方式,如果里面存在一个这个逻辑,最好显式的说明,而不是硬编码 源代码不开放,无法进行扩展和修改,建议作者开发这个jar到github里面,社区一块维护