为什么需要二次开发服务端

把ueditor按官网给的方法集成到FDP项目,ueditor的功能都已可正常使用了。

ueditor是富文本编辑器,功能重点在js客户端这边,目前客户端功能够用够强,无需二次开发。

ueditor的服务端不是他的重点,提供的服务代码更多是演示性的代码,不完全能满足项目的要求,所以要二次开发服务端。

服务端二次开发的目标

  1. 文件存储:与FDP的“文件存储服务”对接,可把文件存储到“文件存储服务”中。 “文件存储服务”的具体实现有多种,如阿里云OSS、本地文件夹、NFS、FTP、FastDFS。
  2. 上传图片:有权限认证,上传时带token才可上传,禁止匿名上传文件。
  3. 访问控制:普通图片可匿名访问。存储在“安全区”的身份证图片,带token才可访问。
  4. 缩略图:要与FDP的实时生成缩略图功能相结合。
  5. 分布式存储:文件分布式存储,由“文件存储服务”解决分布式存储。
  6. 分布式部署:在大型系统架构中,由多个子系统组成,upload子系统专负责上传下载,upload系统要支持分布式部署。

ueditor默认存储方式是,按config.json的配置,把文件存储在服务器的磁盘上,目录结构类似于:/ueditor/jsp/upload/image/{yyyy}{mm}{dd}/{time}{rand:6}。这块是二次开发改造的重点。

停用的功能

ueditor服务端支持以下功能。

  1. 上传图片
  2. 涂鸦图片上传
  3. 抓取远程图片
  4. 列出指定目录下的图片
  5. 上传视频

本次二开只覆盖“上传图片”功能,其它功能都停用。如:涂鸦图片、抓取远程图片、列出指定目录下的图片,目前不需要这些功能,在客户端都不调用此功能。如果以后需要,请接着开发。

问:为什么不自己写一个简单、干净的服务端,而要二次开发?

答:自已写新的服务端工作量太多,ueditor的客户端与服务端通信时有内部约定好的数据传输格式,想都承接下来是有难度的。

下面记录二次开发的重点内容。

服务端的源代码

UEditor的初次安装 文档中,以准备了服务羰的源代码,下面将修改其中几个文件,实现二次开发。

在页面上使用UEditor编辑器

具体使用方法,请参看ueditor的官方文档与演示,需要js的知识,内容较多。以下只是一个简单的演示。

<div class="controls">
	<!-- 加载编辑器的容器 -->
    <script type="text/plain" id="container" name="introduction">${detail.introduction}</script>
    <!-- 配置文件 -->
    <script type="text/javascript" src="${ctxStatic}/baiduUEditor1.4.3.2/ueditor.config.js"></script>
    <!-- 编辑器源码文件 -->
    <script type="text/javascript" src="${ctxStatic}/baiduUEditor1.4.3.2/ueditor.all.min.js"></script>
    <!-- 实例化编辑器 -->
    <script type="text/javascript">
        var ue = UE.getEditor('container');
    </script>
</div>

UEditor客户端-配置服务端的接口路径

编辑/shop-web-static/src/main/webapp/static/baiduUEditor1.4.3.2/ueditor.config.js

UEditor服务端-编辑ueditor_upload_config.json文件

使用UEditor原生的config.json文件,改名为ueditor_upload_config.json文件,放入项目的resources目录,项目的配置文件都应放在这里。

目的:访问图片时,走FDP的FileStorageDownloadServlet,可实现访问控制与实现生成缩略图。

第11行
    "imageUrlPrefix": "", /* 图片访问路径前缀 */
替换为
    "imageUrlPrefix": "####", /* 图片访问路径前缀,####会被Global.getConfig("filestorage.dir")替换 */

开发UEditorController

UEditorController是ueditor服务端的入口,客户端会来调用。

com.sicheng.upload.fileupload.web.UEditorController

  1. 统一上传入口,UEditor客户端向服务端的/upload/ueditorServer.htm接口上传文件。
  2. 替代controller.jsp,只留一个入口。
  3. 实现token验证
@Controller
public class UEditorController extends BaseController {
 
 /**
  * 处理上传的入口方法 
  */
 @RequestMapping(value = "/upload/ueditorServer")
 public void upload(HttpServletRequest request, HttpServletResponse response) throws IOException {
 
	//验证accessKey,合格才可上传
	String accessKey=request.getParameter("accessKey");
	if(StringUtils.isBlank(accessKey) || (!AccessKey.verification(accessKey)) ){
		response.setCharacterEncoding( "utf-8" );
		response.setHeader("Content-Type" , "text/html");
		PrintWriter out = response.getWriter();
	    out.write( new BaseState(false, AppInfo.ACCESSKEY_ERROR).toJSONString());
	    return ;
	}
 
 	//以下代码是ueditor自带的,原样保留在这里,未做改动
  	response.setCharacterEncoding( "utf-8" );
 	response.setHeader("Content-Type" , "text/html");
  	String rootPath = request.getContextPath();
  	PrintWriter out = response.getWriter();
  	out.write( new ActionEnter( R.getRequest(), rootPath ).exec() );
 	}
}

停用controller.jsp

删除controller.jsp,入口不再走controller.jsp,改走UEditorController,upload系统的所有Controller所有一块,方便管理。

开发MyCommonsMultipartResolver

目标:让ueditor的服务端与spring和睦相处

自定义MyCommonsMultipartResolver类: public class MyCommonsMultipartResolver extends CommonsMultipartResolver

重写父类的isMultipart方法

目标:对指定的url,不使用spring的CommonsMultipartResolver来处理上传,放过,由后面的业务程序自行处理。

理由:因本项目中使用了ueditor,接收上传的服务端,是一套独立,不能让spring来参与处理,否则会有冲突的。

原理:org.springframework.web.multipart.commons.CommonsMultipartResolver的源代码,其中有个public boolean isMultipart(HttpServletRequest request)方法,此方法控制着Spring是否对用户的Request进行文件上传处理,于是自定义一个我们自己的CommonsMultipartResolver,继承自org.springframework.web.multipart.commons.CommonsMultipartResolver,并重写其中的public boolean isMultipart(HttpServletRequest request),在方法中对当前的request进行判断,如果是ueditor上传专用的url,则返回false,告知Spring不需要再对当前的request进行文件上传处理;如果不是,则直接调用父类的isMultipart方法。

配置MyCommonsMultipartResolver

修改/shop-web-upload/src/main/resources/spring-mvc-upload.xml文件,加入以下内容,让MyCommonsMultipartResolver工作起来。

 <bean id="multipartResolver" class="com.sicheng.upload.component.MyCommonsMultipartResolver">  
 <!-- <property name="maxUploadSize" value="1048576000" /> --> 
 <property name="maxUploadSize" value="${web.maxUploadSize}" />
 <property name="excludeUrls">
         <list>
          <value>/upload/ueditorServer.htm</value><!-- 为了百度富文本编辑器ueditor而放过此url -->
         </list>
        </property>  
 </bean>

修改ConfigManager.java

com.baidu.ueditor.ConfigManager

指明配置文件名

private static final String configFileName = "ueditor_upload_config.json" ;

添加 insertPathVar方法

 /**
  * 替换变量  赵磊
  * 使用Global.getConfig("filestorage.dir")替换####
  * 
  * @param configContent  json配置文件
  * @param contextPath2  应用的根路径  contextPath
  * @return
  */
 private String insertPathVar(String configContent, String contextPath2) {
  String path="";
  if(contextPath!=null && contextPath.length()>0){
   path=contextPath2 + Global.getConfig("filestorage.dir");
  }else{
   path=Global.getConfig("filestorage.dir");
  }
 
 configContent=configContent.replaceAll("####", path);
 return configContent;
 }

添加getConfigPath方法

 /**
  * 这方法是拼全config.json的绝对路径。 赵磊
  * UEditor官方给的方案是:parentPath是controller.jsp 的目录路径,所以限制config.json 必须和controller.jsp在同一个目录下。
  * 赵磊的替代方案:用spring controller代替controller.jsp,在类路径下查找config.json。  
  */
 private String getConfigPath () {
 //获取类的根路径classes/(重要)
  URL url = ConfigManager.class.getResource("/");
  String rootClassPath=url.getPath();//如果路径有空格会使用 %20替代
  try {
 //  %20 转换为 空格(方案二)
   rootClassPath = java.net.URLDecoder.decode(rootClassPath, "UTF-8");
  } catch (UnsupportedEncodingException e1) {
   e1.printStackTrace();
  }
  System.out.println(rootClassPath);
  String path=rootClassPath + File.separator + ConfigManager.configFileName;
 return path;
 }


替换
####

filestorage.dir是文件存储的目录(当使用本地存储方案时),可通过Global.getConfig("filestorage.dir")获取。配置在fdp.properties文件。

由于filestorage.dir可被修改,想只修改一次,所有这里有获取并替换。

修改AppInfo.java

com.baidu.ueditor.define.AppInfo

加入

public static final int ACCESSKEY_ERROR = 403;

put( AppInfo. ACCESSKEY_ERROR , "accessKey\u9a8c\u8bc1\u5931\u8d25" );


修改 BinaryUploader.java

注释了80行

修改StorageManager.java

com.baidu.ueditor.upload.StorageManager

StorageManager类负责文件存储工作,在这里与FDP的“文件存储服务”对接,实现把文件写入“文件存储服务”的目的。

本类的3个核心方法,都做了修改,是修改量最大的一个文件。具体修改因较多,请看源码吧。