Research on ueditor file upload

I have written a version of ueditor before. I feel that ueditor has been upgraded very quickly and in a twinkling of an eye. Today, a person asked this related question, which happens to be familiar with.
First of all, I won't talk about the most basic usage, just the file uploaded.
First of all, the file upload has changed a lot, so let's talk about its usage slowly.
1. The file structure of the Java version in the jsp directory is as follows:
       
As you can see from this, there is a controller.jsp, a config.json, and a bunch of jar files, which are not consistent with previous versions.
2. Reference to the jar package for the Maven project
If you don't use jar packages, it's easy to copy files directly, but maven's way, this jar doesn't exist on the Internet. Thankfully, Maven provides system-based dependencies:
       
  1. < dependency>  
  2.              < groupId> com.baidu.ueditor </groupId >  
  3.              < artifactId> ueditor </artifactId >  
  4.              < version> 1.1.1 </version >  
  5.              < scope> system </scope >  
  6.              < systemPath> ${basedir}/ src/main/webapp /WEB-INF/lib/ ueditor-1.1.1.jar </systemPath >  
  7.          </ dependency>  
The location of maven's jar package is as follows:
       
Other jar s are easy to find.
3. Read controller.jsp file
        
  1. <%@ page language="java" contentType="text/html; charset=UTF-8"  
  2.     import="com.baidu.ueditor.ActionEnter"  
  3.     pageEncoding="UTF-8"%>  
  4. <%@ page trimDirectiveWhitespaces="true" %>  
  5. <%  
  6.   
  7.     request.setCharacterEncoding( "utf-8" );  
  8.     response.setHeader("Content-Type" , "text/html");  
  9.       
  10.     String rootPath = application.getRealPath( "/" );  
  11.       
  12.     out.write( new ActionEnter( request, rootPath ).exec() );  
  13.       
  14. %>  
 
From the code point of view, rootPath is actually the root path of the project, constructs the ActionEnter, and calls the exec function.
Let's look at the code for ActionEnter:
       
  1. package com.baidu.ueditor;  
  2.   
  3. import com.baidu.ueditor.define.ActionMap;  
  4. import com.baidu.ueditor.define.BaseState;  
  5. import com.baidu.ueditor.define.State;  
  6. import com.baidu.ueditor.hunter.FileManager;  
  7. import com.baidu.ueditor.hunter.ImageHunter;  
  8. import com.baidu.ueditor.upload.Uploader;  
  9. import java.util.Map;  
  10. import javax.servlet.http.HttpServletRequest;  
  11. import org.json.JSONObject;  
  12.   
  13. public class ActionEnter  
  14. {  
  15.   private HttpServletRequest request = null;  
  16.   
  17.   private String rootPath = null;  
  18.   private String contextPath = null;  
  19.   
  20.   private String actionType = null;  
  21.   
  22.   private ConfigManager configManager = null;  
  23.   
  24.   public ActionEnter(HttpServletRequest request, String rootPath)  
  25.   {  
  26.     this.request = request;  
  27.     this.rootPath = rootPath;  
  28.     //Assignment of action.  
  29.     this.actionType = request.getParameter("action");  
  30.     this.contextPath = request.getContextPath();  
  31.     //Build configManager class  
  32.     this.configManager = ConfigManager.getInstance(this.rootPath, this.contextPath, request.getRequestURI());  
  33.   }  
  34.   
  35.   public String exec()  
  36.   {  
  37.     //This is in the form of jsonp, which is generally non-cross-domain.  
  38.     String callbackName = this.request.getParameter("callback");  
  39.   
  40.     if (callbackName != null)  
  41.     {  
  42.       if (!validCallbackName(callbackName)) {  
  43.         return new BaseState(false401).toJSONString();  
  44.       }  
  45.   
  46.       return callbackName + "(" + invoke() + ");";  
  47.     }  
  48.   
  49.     return invoke();  
  50.   }  
  51.   
  52.   public String invoke()  
  53.   {  
  54.     //Judging whether action is legal or not, if it returns to an illegal state  
  55.     if ((this.actionType == null) || (!ActionMap.mapping.containsKey(this.actionType))) {  
  56.       return new BaseState(false101).toJSONString();  
  57.     }  
  58.    //If configManager is not found, an error is also reported  
  59.     if ((this.configManager == null) || (!this.configManager.valid())) {  
  60.       return new BaseState(false102).toJSONString();  
  61.     }  
  62.   
  63.     State state = null;  
  64.   
  65.     //Get actionCode  
  66.     int actionCode = ActionMap.getType(this.actionType);  
  67.   
  68.     Map conf = null;  
  69.   
  70.     switch (actionCode)  
  71.     {  
  72.     case 0:  
  73.       return this.configManager.getAllConfig().toString();  
  74.     case 1:  
  75.     case 2:  
  76.     case 3:  
  77.     case 4:  
  78.       //Processing uploaded files  
  79.       conf = this.configManager.getConfig(actionCode);  
  80.       state = new Uploader(this.request, conf).doExec();  
  81.       break;  
  82.     case 5:  
  83.       conf = this.configManager.getConfig(actionCode);  
  84.       String[] list = this.request.getParameterValues((String)conf.get("fieldName"));  
  85.       //Processing online editing  
  86.       state = new ImageHunter(conf).capture(list);  
  87.       break;  
  88.     case 6:  
  89.     case 7:  
  90.       conf = this.configManager.getConfig(actionCode);  
  91.       int start = getStartIndex();  
  92.       //Processing file list  
  93.       state = new FileManager(conf).listFile(start);  
  94.     }  
  95.   
  96.     return state.toJSONString();  
  97.   }  
  98.   
  99.   public int getStartIndex()  
  100.   {  
  101.     String start = this.request.getParameter("start");  
  102.     try  
  103.     {  
  104.       return Integer.parseInt(start); } catch (Exception e) {  
  105.     }  
  106.     return 0;  
  107.   }  
  108.   
  109.   public boolean validCallbackName(String name)  
  110.   {  
  111.     if (name.matches("^[a-zA-Z_]+[\\w0-9_]*$")) {  
  112.       return true;  
  113.     }  
  114.   
  115.     return false;  
  116.   }  
  117. }  
Let's look at this function slowly: First, we call the request.getContextPath() and request.getRequestURI() functions in the constructor.
Assuming our project's contextPath is: test, the return values of the following two functions are as follows:
  request.getContextPath                   /test
   request.getRequestURI           /test/resources/ueditor/jsp/controller.jsp
Let's first look at the ConfigManager class.
   
  1. package com.baidu.ueditor;  
  2.   
  3. import java.io.BufferedReader;  
  4. import java.io.File;  
  5. import java.io.FileInputStream;  
  6. import java.io.FileNotFoundException;  
  7. import java.io.IOException;  
  8. import java.io.InputStreamReader;  
  9. import java.io.UnsupportedEncodingException;  
  10. import java.util.HashMap;  
  11. import java.util.Map;  
  12. import org.json.JSONArray;  
  13. import org.json.JSONObject;  
  14.   
  15. public final class ConfigManager  
  16. {  
  17.   private final String rootPath;  
  18.   private final String originalPath;  
  19.   private final String contextPath;  
  20.   private static final String configFileName = "config.json";  
  21.   private String parentPath = null;  
  22.   private JSONObject jsonConfig = null;  
  23.   private static final String SCRAWL_FILE_NAME = "scrawl";  
  24.   private static final String REMOTE_FILE_NAME = "remote";  
  25.   
  26.   private ConfigManager(String rootPath, String contextPath, String uri)  
  27.     throws FileNotFoundException, IOException  
  28.   {  
  29.     rootPath = rootPath.replace("\\", "/");  
  30.   
  31.     this.rootPath = rootPath;  
  32.     this.contextPath = contextPath;  
  33.     //This place should pay special attention to the fact that originalPath is actually the path of controller.jsp  
  34.     if (contextPath.length() > 0)  
  35.       this.originalPath = (this.rootPath + uri.substring(contextPath.length()));  
  36.     else {  
  37.       this.originalPath = (this.rootPath + uri);  
  38.     }  
  39.   
  40.     initEnv();  
  41.   }  
  42.   
  43.   public static ConfigManager getInstance(String rootPath, String contextPath, String uri)  
  44.   {  
  45.     try  
  46.     {  
  47.       return new ConfigManager(rootPath, contextPath, uri); } catch (Exception e) {  
  48.     }  
  49.     return null;  
  50.   }  
  51.   
  52.   public boolean valid()  
  53.   {  
  54.     return this.jsonConfig != null;  
  55.   }  
  56.   
  57.   public JSONObject getAllConfig()  
  58.   {  
  59.     return this.jsonConfig;  
  60.   }  
  61.   
  62.   public Map<String, Object> getConfig(int type)  
  63.   {  
  64.     Map conf = new HashMap();  
  65.     String savePath = null;  
  66.     //Resolve config.json configuration files according to different code s  
  67.     switch (type)  
  68.     {  
  69.     case 4:  
  70.       conf.put("isBase64""false");  
  71.       conf.put("maxSize", Long.valueOf(this.jsonConfig.getLong("fileMaxSize")));  
  72.       conf.put("allowFiles", getArray("fileAllowFiles"));  
  73.       conf.put("fieldName"this.jsonConfig.getString("fileFieldName"));  
  74.       savePath = this.jsonConfig.getString("filePathFormat");  
  75.       break;  
  76.     case 1:  
  77.       conf.put("isBase64""false");  
  78.       conf.put("maxSize", Long.valueOf(this.jsonConfig.getLong("imageMaxSize")));  
  79.       conf.put("allowFiles", getArray("imageAllowFiles"));  
  80.       conf.put("fieldName"this.jsonConfig.getString("imageFieldName"));  
  81.       savePath = this.jsonConfig.getString("imagePathFormat");  
  82.       break;  
  83.     case 3:  
  84.       conf.put("maxSize", Long.valueOf(this.jsonConfig.getLong("videoMaxSize")));  
  85.       conf.put("allowFiles", getArray("videoAllowFiles"));  
  86.       conf.put("fieldName"this.jsonConfig.getString("videoFieldName"));  
  87.       savePath = this.jsonConfig.getString("videoPathFormat");  
  88.       break;  
  89.     case 2:  
  90.       conf.put("filename""scrawl");  
  91.       conf.put("maxSize", Long.valueOf(this.jsonConfig.getLong("scrawlMaxSize")));  
  92.       conf.put("fieldName"this.jsonConfig.getString("scrawlFieldName"));  
  93.       conf.put("isBase64""true");  
  94.       savePath = this.jsonConfig.getString("scrawlPathFormat");  
  95.       break;  
  96.     case 5:  
  97.       conf.put("filename""remote");  
  98.       conf.put("filter", getArray("catcherLocalDomain"));  
  99.       conf.put("maxSize", Long.valueOf(this.jsonConfig.getLong("catcherMaxSize")));  
  100.       conf.put("allowFiles", getArray("catcherAllowFiles"));  
  101.       conf.put("fieldName"this.jsonConfig.getString("catcherFieldName") + "[]");  
  102.       savePath = this.jsonConfig.getString("catcherPathFormat");  
  103.       break;  
  104.     case 7:  
  105.       conf.put("allowFiles", getArray("imageManagerAllowFiles"));  
  106.       conf.put("dir"this.jsonConfig.getString("imageManagerListPath"));  
  107.       conf.put("count", Integer.valueOf(this.jsonConfig.getInt("imageManagerListSize")));  
  108.       break;  
  109.     case 6:  
  110.       conf.put("allowFiles", getArray("fileManagerAllowFiles"));  
  111.       conf.put("dir"this.jsonConfig.getString("fileManagerListPath"));  
  112.       conf.put("count", Integer.valueOf(this.jsonConfig.getInt("fileManagerListSize")));  
  113.     }  
  114.   
  115.     conf.put("savePath", savePath);  
  116.     conf.put("rootPath"this.rootPath);  
  117.   
  118.     return conf;  
  119.   }  
  120.   
  121.   //Load config.json configuration file  
  122.   private void initEnv()  
  123.     throws FileNotFoundException, IOException  
  124.   {  
  125.     File file = new File(this.originalPath);  
  126.   
  127.     if (!file.isAbsolute()) {  
  128.       file = new File(file.getAbsolutePath());  
  129.     }  
  130.   
  131.     this.parentPath = file.getParent();  
  132.   
  133.     String configContent = readFile(getConfigPath());  
  134.     try  
  135.     {  
  136.       JSONObject jsonConfig = new JSONObject(configContent);  
  137.       this.jsonConfig = jsonConfig;  
  138.     } catch (Exception e) {  
  139.       this.jsonConfig = null;  
  140.     }  
  141.   }  
  142.   
  143.   private String getConfigPath()  
  144.   {  
  145.     return this.parentPath + File.separator + "config.json";  
  146.   }  
  147.   
  148.   private String[] getArray(String key)  
  149.   {  
  150.     JSONArray jsonArray = this.jsonConfig.getJSONArray(key);  
  151.     String[] result = new String[jsonArray.length()];  
  152.   
  153.     int i = 0for (int len = jsonArray.length(); i < len; i++) {  
  154.       result[i] = jsonArray.getString(i);  
  155.     }  
  156.   
  157.     return result;  
  158.   }  
  159.   
  160.   
  161.   //Read the contents of config.json  
  162.   private String readFile(String path)  
  163.     throws IOException  
  164.   {  
  165.     StringBuilder builder = new StringBuilder();  
  166.     try  
  167.     {  
  168.       InputStreamReader reader = new InputStreamReader(new FileInputStream(path), "UTF-8");  
  169.       BufferedReader bfReader = new BufferedReader(reader);  
  170.   
  171.       String tmpContent = null;  
  172.   
  173.       while ((tmpContent = bfReader.readLine()) != null) {  
  174.         builder.append(tmpContent);  
  175.       }  
  176.   
  177.       bfReader.close();  
  178.     }  
  179.     catch (UnsupportedEncodingException localUnsupportedEncodingException)  
  180.     {  
  181.     }  
  182.   
  183.     return filter(builder.toString());  
  184.   }  
  185.   
  186.   private String filter(String input)  
  187.   {  
  188.     return input.replaceAll("/\\*[\\s\\S]*?\\*/""");  
  189.   }  
  190. }  
 
Let's look at the Uploader function again. It's actually very simple:
   
  1. package com.baidu.ueditor.upload;  
  2.   
  3. import com.baidu.ueditor.define.State;  
  4. import java.util.Map;  
  5. import javax.servlet.http.HttpServletRequest;  
  6.   
  7. public class Uploader  
  8. {  
  9.   private HttpServletRequest request = null;  
  10.   private Map<String, Object> conf = null;  
  11.   
  12.   public Uploader(HttpServletRequest request, Map<String, Object> conf) {  
  13.     this.request = request;  
  14.     this.conf = conf;  
  15.   }  
  16.   
  17.   public final State doExec() {  
  18.     String filedName = (String)this.conf.get("fieldName");  
  19.     State state = null;  
  20.   
  21.     if ("true".equals(this.conf.get("isBase64")))  
  22.       state = Base64Uploader.save(this.request.getParameter(filedName),   
  23.         this.conf);  
  24.     else {  
  25.       state = BinaryUploader.save(this.request, this.conf);  
  26.     }  
  27.   
  28.     return state;  
  29.   }  
  30. }  
   
This is easy to understand, so let's move on to the BinaryUploader class:
     
  1. package com.baidu.ueditor.upload;  
  2.   
  3. import com.baidu.ueditor.PathFormat;  
  4. import com.baidu.ueditor.define.BaseState;  
  5. import com.baidu.ueditor.define.FileType;  
  6. import com.baidu.ueditor.define.State;  
  7. import java.io.IOException;  
  8. import java.io.InputStream;  
  9. import java.util.Arrays;  
  10. import java.util.List;  
  11. import java.util.Map;  
  12. import javax.servlet.http.HttpServletRequest;  
  13. import org.apache.commons.fileupload.FileItemIterator;  
  14. import org.apache.commons.fileupload.FileItemStream;  
  15. import org.apache.commons.fileupload.FileUploadException;  
  16. import org.apache.commons.fileupload.disk.DiskFileItemFactory;  
  17. import org.apache.commons.fileupload.servlet.ServletFileUpload;  
  18.   
  19. public class BinaryUploader  
  20. {  
  21.   //Use fileupload to process file uploads  
  22.   public static final State save(HttpServletRequest request, Map<String, Object> conf)  
  23.   {  
  24.     FileItemStream fileStream = null;  
  25.     boolean isAjaxUpload = request.getHeader("X_Requested_With") != null;  
  26.   
  27.     if (!ServletFileUpload.isMultipartContent(request)) {  
  28.       return new BaseState(false5);  
  29.     }  
  30.   
  31.     ServletFileUpload upload = new ServletFileUpload(  
  32.       new DiskFileItemFactory());  
  33.   
  34.     if (isAjaxUpload) {  
  35.       upload.setHeaderEncoding("UTF-8");  
  36.     }  
  37.     try  
  38.     {  
  39.       FileItemIterator iterator = upload.getItemIterator(request);  
  40.   
  41.       while (iterator.hasNext()) {  
  42.         fileStream = iterator.next();  
  43.   
  44.         if (!fileStream.isFormField())  
  45.           break;  
  46.         fileStream = null;  
  47.       }  
  48.   
  49.       if (fileStream == null) {  
  50.         return new BaseState(false7);  
  51.       }  
  52.   
  53.       String savePath = (String)conf.get("savePath");  
  54.       String originFileName = fileStream.getName();  
  55.       String suffix = FileType.getSuffixByFilename(originFileName);  
  56.   
  57.       originFileName = originFileName.substring(0,   
  58.         originFileName.length() - suffix.length());  
  59.       savePath = savePath + suffix;  
  60.   
  61.       long maxSize = ((Long)conf.get("maxSize")).longValue();  
  62.   
  63.       if (!validType(suffix, (String[])conf.get("allowFiles"))) {  
  64.         return new BaseState(false8);  
  65.       }  
  66.   
  67.       savePath = PathFormat.parse(savePath, originFileName);  
  68.   
  69.       String physicalPath = (String)conf.get("rootPath") + savePath;  
  70.   
  71.       //Call storage classes to handle file storage  
  72.       InputStream is = fileStream.openStream();  
  73.       State storageState = StorageManager.saveFileByInputStream(is,   
  74.         physicalPath, maxSize);  
  75.       is.close();  
  76.   
  77.       if (storageState.isSuccess()) {  
  78.         storageState.putInfo("url", PathFormat.format(savePath));  
  79.         storageState.putInfo("type", suffix);  
  80.         storageState.putInfo("original", originFileName + suffix);  
  81.       }  
  82.   
  83.       return storageState;  
  84.     } catch (FileUploadException e) {  
  85.       return new BaseState(false6);  
  86.     } catch (IOException localIOException) {  
  87.     }  
  88.     return new BaseState(false4);  
  89.   }  
  90.   
  91.   private static boolean validType(String type, String[] allowTypes) {  
  92.     List list = Arrays.asList(allowTypes);  
  93.   
  94.     return list.contains(type);  
  95.   }  
  96. }  
 
Storage Manager, we don't look at it, just do some things about file storage, let's analyze the problem of this implementation.
Finally, I'll summarize the benefits of this code and give some suggestions to the authors.
  1. From this point of view, it is impossible to place pictures in external paths, because this implementation determines that they can only be placed under project paths. The biggest problem is that it is possible to accidentally re-offline and lose all the content.
  2. From the implementation point of view, a large number of static calls, basically unable to re-develop, can not flexibly inherit it to deal with personalized things, such as if stored in fastDFS, this need to change the code inside, can not be extended by way of.
  3. Configuration item conversion in config.json is renamed. This place requires the reader to remember two variable names, for example, imagePathFormat becomes savePath, which seems to be quite understandable, but this is obviously not a good way. If there is a logic in it, it's better to explain it explicitly. Not hard-coded
  4. The source code is not open and can not be extended and modified. It is recommended that the author develop this jar into github and maintain it in the community.

Keywords: Java JSON JSP Apache

Added by er0x on Thu, 04 Jul 2019 00:49:33 +0300