河马日记

vuePress-theme-reco Mr.Tian    2019 - 2023
河马日记 河马日记

Choose mode

  • dark
  • auto
  • light
首页
分类
  • 后端
  • 前端
  • 随笔
  • 前后端结合
标签
时间线
联系方式
  • 微信
author-avatar

Mr.Tian

15

文章

5

标签

首页
分类
  • 后端
  • 前端
  • 随笔
  • 前后端结合
标签
时间线
联系方式
  • 微信
  • vue引入ueditor富文本编辑器并实现图片上传

    • 下载UEditor源码
      • 引入项目
        • 使用

        vue引入ueditor富文本编辑器并实现图片上传

        vuePress-theme-reco Mr.Tian    2019 - 2023

        vue引入ueditor富文本编辑器并实现图片上传


        Mr.Tian 2020-06-06 11:52:22 vueueditor

        vue引入ueditor富文本编辑器并实现图片上传

        之前在vue+element-ui项目中需要使用富文本编辑器,开始用的vue-quill-editor插件,后因项目需求,改用百度开源的UEditor,从引入项目到能够实现图片成功上传踩了很多坑,在此记录一下,以便后用。

        # 下载UEditor源码

        可以在UEditor官网下载UEditor源码,下载地址:http://ueditor.baidu.com/website/download.html ,我使用的是官网最新[1.4.3.3 Jsp 版本]版本的包。

        # 引入项目

        下载后解压,将除了jsp的文件夹之外其余的文件和文件夹复制到vue项目中的static用于存放静态文件的目录下,因为我是前后端分离项目,后端使用的是spring boot,所以jsp目录下的文件肯定是不会放在前端的项目中的,包括config.json也放在后端用于解析。

        接下来处理jsp文件夹中的文件,将jsp目录下的lib目中的ueditor.jar文件中的所有类全部拿出来,我使用的是JD-GUI反编译了一下,放到后端项目中,然后在controller层新建一个UeditorController.java的类

        package yourpackage;
        
        import yourpath.ActionEnter;
        import org.springframework.web.bind.annotation.CrossOrigin;
        import org.springframework.web.bind.annotation.RequestMapping;
        import org.springframework.web.bind.annotation.ResponseBody;
        import org.springframework.web.bind.annotation.RestController;
        
        import javax.servlet.http.HttpServletRequest;
        import java.io.UnsupportedEncodingException;
        
        /**
         * @program: 
         * @description: 处理请求
         * @author: tiandonghui
         * @create: 2020/4/6 13:10
         **/
        
        @RestController
        @CrossOrigin
        @RequestMapping("/ueditor")
        public class UeditorController {
        
            @RequestMapping(value = "/exec")
            @ResponseBody
            public String exec(HttpServletRequest request) throws UnsupportedEncodingException {
                request.setCharacterEncoding("utf-8");
                String rootPath = request.getRealPath("/");
                return new ActionEnter( request, rootPath ).exec();
            }
        }
        
        
        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

        jsp文件夹下的config.json文件放到yourpath/main/resources目录下,这里检查下config.json中是否存在中文注释,如果存在最好删掉,这对后面为了部署到服务器上时能够解析到config.json而对config.json进行的处理有影响,存在中文可能config.json解析出错,导致UEditor获取配置失败从而加载不出来,所以删除config.json中的中文注释,接下来修改ConfigManager.java类,如下:

        package yourpackage;
        
        import yourpath.ueditor.define.ActionMap;
        import org.json.JSONArray;
        import org.json.JSONException;
        import org.json.JSONObject;
        
        import java.io.*;
        import java.util.HashMap;
        import java.util.Map;
        
        /**
         * @program:
         * @description:
         * @author: tiandonghui
         * @create: 2020/4/6 13:14
         **/
        public 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 final static String SCRAWL_FILE_NAME = "scrawl";
            private final static 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;
                this.originalPath = "src/main/resources/config.json";
                this.initEnv();
        
            }
        
            /**
             * 配置管理器构造工厂
             * @param rootPath 服务器根路径
             * @param contextPath 服务器所在项目路径
             * @param uri 当前访问的uri
             * @return 配置管理器实例或者null
             */
            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 ) throws JSONException {
        
                Map<String, Object> conf = new HashMap<String, Object>();
                String savePath = null;
                String localSavePathPrefix = 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" ) );
                        conf.put( "fieldName", this.jsonConfig.getString( "imageFieldName" ) );
                        savePath = this.jsonConfig.getString( "imagePathFormat" );
                        localSavePathPrefix = this.jsonConfig.getString("localSavePathPrefix");
                        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" );
                        localSavePathPrefix = this.jsonConfig.getString("localSavePathPrefix");
                        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" );
                        localSavePathPrefix = this.jsonConfig.getString("localSavePathPrefix");
                        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( "localSavePathPrefix", localSavePathPrefix );
                conf.put( "rootPath", this.rootPath );
        
                return conf;
        
            }
        
            private void initEnv () throws FileNotFoundException, IOException {
                String xmlString = "";
                try {
                    // 为了在服务器上能够正确解析config.json,所以对config.json进行处理
                    StringBuffer content = new StringBuffer();
                    InputStream stream = getClass().getClassLoader().getResourceAsStream("config.json");
                    BufferedReader br = new BufferedReader(new InputStreamReader(stream));
                    String s = "";
                    while ((s = br.readLine()) != null) {
                        xmlString = xmlString + s;
                    }
                    br.close();
                } catch (Exception e) {
                    throw new RuntimeException(e.getMessage());
                }
                try{
                    JSONObject jsonConfig = new JSONObject( xmlString );
                    this.jsonConfig = jsonConfig;
                } catch ( Exception e ) {
                    this.jsonConfig = null;
                }
        
            }
        
            private String getConfigPath () {
                return this.parentPath + File.separator + ConfigManager.configFileName;
            }
        
            private String[] getArray ( String key ) throws JSONException {
        
                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]*?\\*/", "" );
        
            }
        }
        
        
        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
        117
        118
        119
        120
        121
        122
        123
        124
        125
        126
        127
        128
        129
        130
        131
        132
        133
        134
        135
        136
        137
        138
        139
        140
        141
        142
        143
        144
        145
        146
        147
        148
        149
        150
        151
        152
        153
        154
        155
        156
        157
        158
        159
        160
        161
        162
        163
        164
        165
        166
        167
        168
        169
        170
        171
        172
        173
        174
        175
        176
        177
        178
        179
        180
        181
        182
        183
        184
        185
        186
        187
        188
        189
        190
        191
        192
        193
        194
        195
        196
        197
        198
        199
        200
        201
        202
        203
        204
        205
        206
        207
        208
        209
        210
        211
        212
        213
        214

        接下来配置StorageManager.java文件

        package yourpackage;
        
        import yourpath.ueditor.define.AppInfo;
        import yourpath.ueditor.define.BaseState;
        import yourpath.ueditor.define.State;
        import org.apache.commons.io.FileUtils;
        import org.springframework.boot.context.properties.ConfigurationProperties;
        import org.springframework.stereotype.Component;
        import org.springframework.web.multipart.MultipartFile;
        
        import javax.annotation.PostConstruct;
        import javax.annotation.Resource;
        import java.io.*;
        
        @Component
        @ConfigurationProperties(prefix="nginx")
        public class StorageManager {
        
        	public static StorageManager storageManager;
        
        	public static final int BUFFER_SIZE = 8192;
        	
        	private static String fileurl;
        
        	public static String getFileurl() {
        		return fileurl;
        	}
        
        	public static void setFileurl(String fileurl) {
        		StorageManager.fileurl = fileurl;
        	}
        
        	public static int getBufferSize() {
        		return BUFFER_SIZE;
        	}
        
        	@PostConstruct
        	public void init(){
        		storageManager = this;//工具类的实例赋值给fileUtils
        		storageManager.pubAttachService=this.pubAttachService;//会激活Spring对Dao的管理并赋给此类
        		System.out.println("工具类已经初始化了,被纳入spring管理");
        	}
        
        	public StorageManager() {
        
        	}
        
        	public static State saveBinaryFile(byte[] data, String path) {
        		File file = new File(path);
        
        		State state = valid(file);
        
        		if (!state.isSuccess()) {
        			return state;
        		}
        
        		try {
        			BufferedOutputStream bos = new BufferedOutputStream(
        					new FileOutputStream(file));
        			bos.write(data);
        			bos.flush();
        			bos.close();
        		} catch (IOException ioe) {
        			return new BaseState(false, AppInfo.IO_ERROR);
        		}
        
        		state = new BaseState(true, file.getAbsolutePath());
        		state.putInfo( "size", data.length );
        		state.putInfo( "title", file.getName() );
        		return state;
        	}
        
        	public static State saveFileByInputStream(MultipartFile file,String picName) {
        
        		State state = null;
        		File tmpFile = getTmpFile();
            /**
            * 此处调用文件上传服务,并获取返回结果返回
            */
        
            //上传服务
        
            // 上传结果成功true,失败false
        		boolean success = true;
            //如果上传成功
            if (success) {
              state = new BaseState(true);
              state.putInfo( "size", tmpFile.length() );
              state.putInfo( "title", picName);//文件名填入此处
              state.putInfo( "group", "");//所属group填入此处
              state.putInfo( "url", "");//文件访问的url填入此处
              tmpFile.delete();
            }else{
              state = new BaseState(false, 4);
              tmpFile.delete();
            }
        
            return state;
        	}
        
        	private static File getTmpFile() {
        		File tmpDir = FileUtils.getTempDirectory();
        		String tmpFileName = (Math.random() * 10000 + "").replace(".", "");
        		return new File(tmpDir, tmpFileName);
        	}
        
        	private static State saveTmpFile(File tmpFile, String path) {
        		State state = null;
        		File targetFile = new File(path);
        
        		if (targetFile.canWrite()) {
        			return new BaseState(false, AppInfo.PERMISSION_DENIED);
        		}
        		try {
        			FileUtils.moveFile(tmpFile, targetFile);
        		} catch (IOException e) {
        			return new BaseState(false, AppInfo.IO_ERROR);
        		}
        
        		state = new BaseState(true);
        		state.putInfo( "size", targetFile.length() );
        		state.putInfo( "title", targetFile.getName() );
        		
        		return state;
        	}
        
        	private static State valid(File file) {
        		File parentPath = file.getParentFile();
        
        		if ((!parentPath.exists()) && (!parentPath.mkdirs())) {
        			return new BaseState(false, AppInfo.FAILED_CREATE_FILE);
        		}
        
        		if (!parentPath.canWrite()) {
        			return new BaseState(false, AppInfo.PERMISSION_DENIED);
        		}
        
        		return new BaseState(true);
        	}
        }
        
        
        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
        117
        118
        119
        120
        121
        122
        123
        124
        125
        126
        127
        128
        129
        130
        131
        132
        133
        134
        135
        136
        137
        138
        139
        140
        141

        接下来处理前端,修改ueditor.config.js文件的serverUrl为访问后端的url,即你的前缀 + "ueditor/exec"。在项目中正式引入,如果项目中有动态加载初始资源的init.js文件,可以直接在此引入。

        // ueditor
        window.SITE_CONFIG.cdnUrl + '/static/ueditor/ueditor.config.js',
        window.SITE_CONFIG.cdnUrl + '/static/ueditor/ueditor.all.js',
        window.SITE_CONFIG.cdnUrl + '/static/ueditor/jquery-2.2.3.min.js',
        window.SITE_CONFIG.cdnUrl + '/static/ueditor/lang/zh-cn/zh-cn.js',
        
        
        1
        2
        3
        4
        5
        6

        # 使用

        如果没有可在main.js中引入,接下来新建一个ueditor.vue文件,我为了区分用途,命名为ue-for-resume.vue,这里可以建立多个文件,以解决同一页面同时实例化多个ueditor插件冲突的问题。

        <template>
          <div>
            <script id="editorForResume" type="text/plain"></script>
          </div>
        </template>
        <script>
        export default {
          name: "ueForResume",
          data() {
            return {
              editor: null
            };
          },
          props: {
            value: "",
            config: {}
          },
          mounted() {
            const _this = this;
            this.editor = window.UE.getEditor("editorForResume", this.config);
            var editor = UE.getEditor('editorForResume');
            editor.ready(function(){
              editor.execCommand('lineheight', 1.75);          //行间距
            });
            // 初始化UE
            this.editor.addListener("ready", function() {
              _this.editor.setContent(_this.value);
              // 确保UE加载完成后,放入内容。
            });
          },
          methods: {
            getUEContent() {
              // 获取内容方法
              return this.editor.getContent();
            },
            getUEContentLength() {
              // 获取内容长度
              return this.editor.getContentTxt().length;
            },
            readyUe(){
              this.editor.setContent('')
            }
          },
          destroyed() {
            this.editor.destroy();
          }
        };
        </script>
        
        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

        至此大功告成,在你需要使用ueditor的页面引入ueditor

        import Ueditor from '@/components/ue-for-resume.vue'
        
        
        1
        2

        components中注册组件

        components: {Ueditor}
        
        
        1
        2

        需要使用的位置

        <Ueditor :value="ueditor.value" :config="ueditor.config" ref="ueForResume"></Ueditor>
        
        
        1
        2