欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

关于富文本编辑器—UEditor(java版)的使用,以及如何将UEditor的文件/图片上传路径改成绝对路径

程序员文章站 2022-06-20 09:39:37
突然发现好久没写博客了,感觉变懒了,是要让自己养成经常写文章的习惯才行。既可以分享自己的所学,和所想,和大家一起讨论,发现自己的不足的问题。 大家可能经常会用到富文本编辑器,今天我要说的是UEditor的使用,这是一个简单易用的开源富文本编辑器。但是对于没有用过的同学而言还是需要稍微了解一下的。 可 ......

突然发现好久没写博客了,感觉变懒了,是要让自己养成经常写文章的习惯才行。既可以分享自己的所学,和所想,和大家一起讨论,发现自己的不足的问题。

大家可能经常会用到富文本编辑器,今天我要说的是ueditor的使用,这是一个简单易用的开源富文本编辑器。但是对于没有用过的同学而言还是需要稍微了解一下的。

可能有些人也知道,ueditor是百度的开源富文本编辑器,当然也有一些其他优秀的富文本编辑器,比如kindeditor,ckeditor之类的,大家可以对比一下使用,但是我今天主要是讲java版的ueditor。

大家可以通过 https://ueditor.baidu.com/website/download.html 选择自己想使用的版本下载。

首先介绍一下它的目录层级:

                   关于富文本编辑器—UEditor(java版)的使用,以及如何将UEditor的文件/图片上传路径改成绝对路径

  • dialogs: 弹出对话框对应的资源和js文件
  • jsp或php或asp或net: 涉及到服务器端操作的后台文件
  • lang: 编辑器国际化显示的文件
  • themes: 样式图片和样式文件
  • third-party: 第三方插件(包括代码高亮,源码编辑等组件)
  • ueditor.all.js: 开发版代码合并的结果,目录下所有文件的打包文件
  • ueditor.all.min.js: ueditor.all.js文件的压缩版,建议在正式部署时采用
  • ueditor.config.js: 编辑器的配置文件,建议和编辑器实例化页面置于同一目录
  • ueditor.parse.js: 编辑的内容显示页面引用,会自动加载表格、列表、代码高亮等样式

我们把ueditor拷贝到我们的项目路径下,与你的页面是在同一个目录下就可以了。

在需要用到ueditor的页面引入下面的文件即可,根据自己的实际路径引入:

    <script type="text/javascript" charset="utf-8" src="../../ueditor/ueditor.config.js"></script>
    <script type="text/javascript" charset="utf-8" src="../../ueditor/ueditor.all.min.js"></script>
    <script type="text/javascript" src="../../ueditor/lang/zh-cn/zh-cn.js"></script>  

在页面需要展示富文本编辑器的位置这样引入:

 1           <!-- 加载编辑器的容器 -->
 2                 <div class="k-field" style="width:1024px;">
 3                     <script id="article_content" name="article_content" type="text/plain" style="width:1024px;height:400px;">
 4                         在这里输入您的内容......
 5                     </script>
 6                 </div>
 7                 <!-- 实例化编辑器 -->
 8                 <script type="text/javascript">
 9                     var ue = ue.geteditor('article_content',{
10                         <!-- 根据个人需要配置,不配置则按默认的 -->
11                         maximumwords:50000,
12                         enableautosave:true,
13                         saveinterval:500
14                     });
15                 </script>   

 

除此之外我们还要引入ueditor所需要的jar包 在 ueditor目录下的  jsp/lib 下有,如果你有引入这些包的话,则无需引入。

关于富文本编辑器—UEditor(java版)的使用,以及如何将UEditor的文件/图片上传路径改成绝对路径

如果你的不是maven项目的话,直接把所有包拷贝到项目下面的lib目录下就可以自动引入了,但是如果是maven项目的话,maven库里没有ueditor的包,那就不能按照平常的方式引入了,可以把ueditor的包拷贝到项目的lib目录下之后,在pom.xml中这样引入:

1         <dependency>
2             <groupid>com.baidu.ueditor</groupid>
3             <artifactid>ueditor</artifactid>
4             <version>1.1.2</version>
5             <scope>system</scope>
6             <systempath>${project.basedir}/src/main/webapp/web-inf/lib/ueditor-1.1.2.jar</systempath>
7         </dependency>

 

上面的步骤都完成之后就可以去对应的页面试一下效果如何。如果没有做任何改动的话就是下面的效果:

 关于富文本编辑器—UEditor(java版)的使用,以及如何将UEditor的文件/图片上传路径改成绝对路径

以上就完成了最简单的ueditor富文本编辑器引入方式,可以体验一下上传图片和文件,都可以正常上传。

但是当你重新部署项目之后会发现,怎么之前上传的文件和图片下载不了也看不到了呢。这就是因为ueditor原生上传文件和图片的方式是上传到项目路径下,那重新部署项目,那不就把原来上传的文件和图片覆盖了咯。

所以为了避免这样的情况发生,我们接下来稍微修改一下ueditor的代码。可能步骤有点多,但是耐心地跟着我的步骤一步步去理解,应该可以很快就知道怎么修改。

通过查看源码,下面我们一步步来追踪:

1. 上传图片或文件时最开始会进入“ueditor/jsp/controller.jsp”,我们查看其代码:

 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     string rootpath = application.getrealpath( "/" );
10     out.write( new actionenter( request, rootpath ).exec() );
11     
12 %>
第9行中的rootpath 是获取 项目的实际路径,并作为 actionenter 构造函数的参数之一实例化对象,在这个构造函数中(如下面的代码)会去初始化改类的成员变量以及通过“configmanager.getinstance”获取配置,
其实是获取 ueditor/jsp/config.json 的配置信息。大家可以去看一下里面配置了一些什么内容,里面也有注释,我这里粘贴图片的配置内容:
1     public actionenter(httpservletrequest request, string rootpath) {
2         this.request = request;
3         this.rootpath = rootpath;
4         this.actiontype = request.getparameter("action");
5         this.contextpath = request.getcontextpath();
6         this.configmanager = configmanager.getinstance(this.rootpath, this.contextpath, request.getrequesturi());
7     }
 1     /* 上传图片配置项 */
 2     "imageactionname": "uploadimage", /* 执行上传图片的action名称 */
 3     "imagefieldname": "upfile", /* 提交的图片表单名称 */
 4     "imagemaxsize": 2048000, /* 上传大小限制,单位b */
 5     "imageallowfiles": [".png", ".jpg", ".jpeg", ".gif", ".bmp"], /* 上传图片格式显示 */
 6     "imagecompressenable": true, /* 是否压缩图片,默认是true */
 7     "imagecompressborder": 1600, /* 图片压缩最长边限制 */
 8     "imageinsertalign": "none", /* 插入的图片浮动方式 */
 9     "imageurlprefix": "", /* 图片访问路径前缀 */
10     "imagepathformat": "/ueditor/jsp/upload/image/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */
11                                 /* {filename} 会替换成原文件名,配置这项需要注意中文乱码问题 */
12                                 /* {rand:6} 会替换成随机数,后面的数字是随机数的位数 */
13                                 /* {time} 会替换成时间戳 */
14                                 /* {yyyy} 会替换成四位年份 */
15                                 /* {yy} 会替换成两位年份 */
16                                 /* {mm} 会替换成两位月份 */
17                                 /* {dd} 会替换成两位日期 */
18                                 /* {hh} 会替换成两位小时 */
19                                 /* {ii} 会替换成两位分钟 */
20                                 /* {ss} 会替换成两位秒 */
21                                 /* 非法字符 \ : * ? " < > | */
22                                 /* 具请体看线上文档: fex.baidu.com/ueditor/#use-format_upload_filename */

 

2. 总共也就几行代码,发现会调用 actionenter 类的 exec() 方法,我进入该方法:

1     public string exec() {
2         string callbackname = this.request.getparameter("callback");
3         if (callbackname != null) {
4             return !this.validcallbackname(callbackname) ? (new basestate(false, 401)).tojsonstring() : callbackname + "(" + this.invoke() + ");";
5         } else {
6             return this.invoke();
7         }
8     }

3. 发现这个方法会调用 该类的 invoke() 方法,我们继续看invoke() 方法:

 1     public string invoke() {
 2         if (this.actiontype != null && actionmap.mapping.containskey(this.actiontype)) {
 3             if (this.configmanager != null && this.configmanager.valid()) {
 4                 state state = null;
 5                 int actioncode = actionmap.gettype(this.actiontype);
 6                 map<string, object> conf = null;
 7                 switch(actioncode) {
 8                 case 0:
 9                     return this.configmanager.getallconfig().tostring();
10                 case 1:
11                 case 2:
12                 case 3:
13                 case 4:
14                     conf = this.configmanager.getconfig(actioncode);
15                     state = (new uploader(this.request, conf)).doexec();
16                     break;
17                 case 5:
18                     conf = this.configmanager.getconfig(actioncode);
19                     string[] list = this.request.getparametervalues((string)conf.get("fieldname"));
20                     state = (new imagehunter(conf)).capture(list);
21                     break;
22                 case 6:
23                 case 7:
24                     conf = this.configmanager.getconfig(actioncode);
25                     int start = this.getstartindex();
26                     state = (new filemanager(conf)).listfile(start);
27                 }
28 
29                 return state.tojsonstring();
30             } else {
31                 return (new basestate(false, 102)).tojsonstring();
32             }
33         } else {
34             return (new basestate(false, 101)).tojsonstring();
35         }
36     }

在上面的这个方法中我们发现,在第7行代码中有一个switch,根据 “actioncode” 的值的不同做一些不同的事情,我们看一下这个值从哪里来的,发现这个值是 通过调用这 个 方法 而得到 “actionmap.gettype(this.actiontype)”,我们进去gettype方法会发现 只是返回一个map的velue值,这个value值就是上的 mapping 里的值,我们通过第1步的配置文件可以知道上传图片的是“uploadimage”

所以返回的value值是 1,而文件“uploadfile”返回的是4。我们再看到上面的switch 可以知道执行this.configmanager.getconfig 重新组装了配置信息并将配置信息作为参数实例化uploader ,并调用该实例 的 doexec()方法。

 1 public final class actionmap {
 2     public static final map<string, integer> mapping = new hashmap<string, integer>() {
 3         {
 4             this.put("config", integer.valueof(0));
 5             this.put("uploadimage", integer.valueof(1));
 6             this.put("uploadscrawl", integer.valueof(2));
 7             this.put("uploadvideo", integer.valueof(3));
 8             this.put("uploadfile", integer.valueof(4));
 9             this.put("catchimage", integer.valueof(5));
10             this.put("listfile", integer.valueof(6));
11             this.put("listimage", integer.valueof(7));
12         }
13     };
14     public static final int config = 0;
15     public static final int upload_image = 1;
16     public static final int upload_scrawl = 2;
17     public static final int upload_video = 3;
18     public static final int upload_file = 4;
19     public static final int catch_image = 5;
20     public static final int list_file = 6;
21     public static final int list_image = 7;
22 
23     public actionmap() {
24     }
25 
26     public static int gettype(string key) {
27         return ((integer)mapping.get(key)).intvalue();
28     }
29 }

4.我进入到 uploader 类,上一步最后就是调用第10行的方法,因为配置里 不是base64 所以执行else里的内容,调用 binaryuploader 的save方法

 1 public class uploader {
 2     private httpservletrequest request = null;
 3     private map<string, object> conf = null;
 4 
 5     public uploader(httpservletrequest request, map<string, object> conf) {
 6         this.request = request;
 7         this.conf = conf;
 8     }
 9 
10     public final state doexec() {
11         string filedname = (string)this.conf.get("fieldname");
12         state state = null;
13         if ("true".equals(this.conf.get("isbase64"))) {
14             state = base64uploader.save(this.request.getparameter(filedname), this.conf);
15         } else {
16             state = binaryuploader.save(this.request, this.conf);
17         }
18 
19         return state;
20     }
21 }

 

5. 我们继续进入到binaryuploader 的save方法 ,我们看到下面的代码的第33行,会去设定 “physicalpath ” 的值,并作为参数 调用 方法: storagemanager.savefilebyinputstream 进行保存,而“physicalpath ” 值是由 配置信息 参数 config 获取。所以我们需要通过修改配置 来 修改这个值就行了。

 

 1     public static final state save(httpservletrequest request, map<string, object> conf) {
 2         fileitemstream filestream = null;
 3         boolean isajaxupload = request.getheader("x_requested_with") != null;
 4         if (!servletfileupload.ismultipartcontent(request)) {
 5             return new basestate(false, 5);
 6         } else {
 7             servletfileupload upload = new servletfileupload(new diskfileitemfactory());
 8             if (isajaxupload) {
 9                 upload.setheaderencoding("utf-8");
10             }
11 
12             try {
13                 for(fileitemiterator iterator = upload.getitemiterator(request); iterator.hasnext(); filestream = null) {
14                     filestream = iterator.next();
15                     if (!filestream.isformfield()) {
16                         break;
17                     }
18                 }
19 
20                 if (filestream == null) {
21                     return new basestate(false, 7);
22                 } else {
23                     string savepath = (string)conf.get("savepath");
24                     string originfilename = filestream.getname();
25                     string suffix = filetype.getsuffixbyfilename(originfilename);
26                     originfilename = originfilename.substring(0, originfilename.length() - suffix.length());
27                     savepath = savepath + suffix;
28                     long maxsize = ((long)conf.get("maxsize")).longvalue();
29                     if (!validtype(suffix, (string[])conf.get("allowfiles"))) {
30                         return new basestate(false, 8);
31                     } else {
32                         savepath = pathformat.parse(savepath, originfilename);
33                         string physicalpath = (string)conf.get("rootpath") + savepath;
34                         inputstream is = filestream.openstream();
35                         state storagestate = storagemanager.savefilebyinputstream(is, physicalpath, maxsize);
36                         is.close();
37                         if (storagestate.issuccess()) {
38                             storagestate.putinfo("url", pathformat.format(savepath));
39                             storagestate.putinfo("type", suffix);
40                             storagestate.putinfo("original", originfilename + suffix);
41                         }
42 
43                         return storagestate;
44                     }
45                 }
46             } catch (fileuploadexception var14) {
47                 return new basestate(false, 6);
48             } catch (ioexception var15) {
49                 return new basestate(false, 4);
50             }
51         }
52     }

6.上面都是在分析代码,下面就开始修改了,我们在配置文件“ueditor/jsp/config.json”里添加 saverootpath ,这就是存储图片或文件的绝对根路径,修改 imageurlprefix 的值,这个链接是要指向 绝对根路径,用于访问图片或文件,大家可以搭一个nginx服务器,或者直接用tomcat也可以,只要能访问都行。

  "saverootpath": "/data/", /* 文件和图片上传绝对根路径*/
    /* 上传图片配置项 */
    "imageactionname": "uploadimage", /* 执行上传图片的action名称 */
    "imagefieldname": "upfile", /* 提交的图片表单名称 */
    "imagemaxsize": 2048000, /* 上传大小限制,单位b */
    "imageallowfiles": [".png", ".jpg", ".jpeg", ".gif", ".bmp"], /* 上传图片格式显示 */
    "imagecompressenable": true, /* 是否压缩图片,默认是true */
    "imagecompressborder": 1600, /* 图片压缩最长边限制 */
    "imageinsertalign": "none", /* 插入的图片浮动方式 */
    "imageurlprefix": "http://xxx", /* 指向saverootpath的url,用于访问图片或文件*/
  "imagepathformat": "/ueditor/jsp/upload/image/{yyyy}{mm}{dd}/{time}{rand:6}",

 

7. 我们在 第 3 步 的invoke() 方法 中调用的  this.configmanager.getconfig(actioncode); 方法里 组装绝对根路径。就是在返回conf之前添加 下面代码中的红色内容。

1         conf.put("savepath", savepath);
2         conf.put("rootpath", this.rootpath);
3         conf.put("saverootpath",this.jsonconfig.getstring("saverootpath"));
4         return conf;

8.  最后我们把 第5步 里的 save 方法里的 physicalpath 值修改一下 ,注释原来的内容,新增下面代码的红色内容。

1 //                      string physicalpath = (string)conf.get("rootpath") + savepath;
2                         string physicalpath = (string)conf.get("saverootpath") + savepath;

这样 我们就把 原来的相对路径改成了我们想要的绝对路径了,大家可以尝试一下。

好了时间不早了,要休息了,大家如果有什么更好的方法,可以在评论去留言,大家一起讨论!如果有什么不妥之处也请大家多多指教!