springboot + extjs +kindeditor富文本编辑器整合+ 解决跨域问题步骤

springboot + extjs6 +kindeditor富文本编辑器整合+ 上传图片解决前后端分离跨域问题步骤

最近在项目中遇到使用富文本编辑器的情况,尝试使用ueditor、wangeditor都以上传图片跨域失败告终,只有kindeditor成功。

基本环境:springboot、extjs6、前后端分离(前端网址:http://localhost:1841  后端网址:http://localhost:9191

现对整过程介绍如下:

一、Extjs 整合kindeditor

1.引入js

<script type="text/javascript" charset="utf-8" src="resources/js/kindeditor/kindeditor-all.js"></script>
<script type="text/javascript" charset="utf-8" src="resources/js/kindeditor/lang/zh-CN.js"> </script>

2.在extjs 中添加kindeditor控件KindEditor.js:

文件放在extjs6路径如下:ext\packages\ux\classic\src\KindEditor.js

Ext.define('Ext.ux.KindEditor', {
    extend: 'Ext.form.field.TextArea',
    alias: 'widget.kindeditor',//xtype名称
    initComponent: function () {
        this.html = "<textarea id='" + this.getId() + "-input' name='" + this.name + "'></textarea>";
        this.callParent(arguments);
        var callback = "http://localhost:1841/redirect.html";
        this.on("boxready", function (t) {
            this.inputEL = Ext.get(this.getId() + "-input");

            this.editor = KindEditor.create('textarea[name="' + this.name + '"]', {
                height: t.getHeight()-18,//有底边高度,需要减去
                width: t.getWidth() - t.labelWidth,//宽度需要减去label的宽度
                basePath: '/resources/js/kindeditor/',
                uploadJson: 'http://localhost:9191/KindEditorUpmethod?callBackPath='+callback,//路径自己改一下
                uploadJsonMultiimage:'http://localhost:9191/KindEditorUpmethodMulti?callBackPath='+callback,//路径自己改一下
                //fileManagerJson: '/Content/Plugin/kindeditor-4.1.5/asp.net/file_manager_json.ashx',//这里可以多设一个多图片上传路径
                resizeType: 0,
                wellFormatMode: true,
                //newlineTag: 'br',
                //allowFileManager: true,
                allowPreviewEmoticons: true,
                allowImageUpload: true,
                items: [
                    'source', '|', 'undo', 'redo', '|', 'justifyleft', 'justifycenter', 'justifyright',
                    'justifyfull', 'insertorderedlist', 'insertunorderedlist', '|',
                    'formatblock', 'fontname', 'fontsize', '|', 'forecolor', 'bold',
                    'italic', 'underline', 'lineheight', '|', 'image', 'multiimage',
                    'table', 'emoticons',
                    'link', 'unlink', 'fullscreen'
                ]
            });
        });
        this.on("resize", function (t, w, h) {
            this.editor.resize(w - t.labelWidth, h-18);
        });
    },
    setValue: function (value) {
        if (this.editor) {
            this.editor.html(value);
        }
    },
    reset: function () {
        if (this.editor) {
            this.editor.html('');
        }
    },
    setRawValue: function (value) {
        if (this.editor) {
            this.editor.text(value);
        }
    },
    getValue: function () {
        if (this.editor) {
            return this.editor.html();
        } else {
            return ''
        }
    },
    getRawValue: function () {
        if (this.editor) {
            return this.editor.text();
        } else {
            return ''
        }
    }
});

3.在ext的编辑窗口中调用:

 {
				   bind: {
					   fieldLabel: '{i18n.content1}'
				   },
				   name: "content",
				  // id: "content",
				   xtype: 'kindeditor',
				   ref:'content',
				   //anchor: '-20',
				   margin: '0 5 5 5',
				   id:'noticecontent',
				   height: 250,
				   width: 400
			   }

4.ext的controller中保存:

saveNotice:function(button){
        //新增点击保存时触发
        var me=this;
      	var win=button.up('window');
		var form=win.down('form').getForm();
		// var vendorValue=form.findField('vendor').getRawValue();
		// form.findField('vendor').setValue(vendorValue);
		var au= win.down('hiddenfield[name=au]').getValue();
		var Url='';
		if(au=='a')
		{
			Url='sys/notice/saveNotice';
		}
		else
		{
			Url='sys/notice/updateNotice';
		}
		var objvalues=form.getValues();
		objvalues.content=form.findField('content').getValue(); //重新给content赋值,默认用form.getValues(),不包含html格式,需单独再赋一遍值,下次编辑时html格式才能正常显示
      	if (form.isValid()) {
              Ext.Ajax.request({
      		    url:Url,
      		    params: objvalues,
      		    method:'POST',
      		    success: function(response){
      		    	var o = Ext.decode(response.responseText.toString());
      		    	if (o.success == true) {
      		    		var main=Ext.ComponentQuery.query('noticelist',null,true);
       		    		main.getViewModel().getStore("noticeStore").load();
                          win.close();
      		    		
      		    	}else{
      		    		Ext.Msg.alert("警告", o.message);
      		    	}
      		    }
      		});
          }
      }

注意:

        var objvalues=form.getValues();
        objvalues.content=form.findField('content').getValue(); //重新给content赋值,默认用form.getValues(),不包含html格式,需单独再赋一遍值,下次编辑时html格式才能正常显示

Extjs端代码基本完成。

5.创建redirect.html 一定要要和这个页面平级,redirect.html的作用是解决frame域的问题 。

借鉴网址:https://blog.csdn.net/weixin_38658548/article/details/105288953

这个页面不要操作直接粘贴就行了

<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>ie </title>
    <script type="text/javascript">
        function getParameter(val) {
            var uri = decodeURI(window.location.search);
            var re = new RegExp("" + val + "=([^&?]*)", "ig");
            return ((uri.match(re)) ? (uri.match(re)[0].substr(val.length + 1)) : null);
        }

        var upload_callback = function() {
            var error = getParameter("error");
            error = parseInt(error)
            var dataObject;
            if(error==0){
                var url = getParameter("url");
                dataObject = {"error": error, "url": url};
            }else{
                var message = getParameter("message");
                dataObject = {"error": error, "message": message};
            }
            var data =  JSON.stringify(dataObject)
            document.getElementsByTagName("body")[0].innerHTML = '<pre>' + data  + '</pre>';
        }
    </script>
</head>
<body onload="upload_callback();">
</body>
</html>

放到前端的根目录:http://localhost:1841/redirect.html

二、后台部分

1.mavaen依赖

		<dependency>
			<groupId>commons-fileupload</groupId>
			<artifactId>commons-fileupload</artifactId>
			<version>1.3.3</version>
		</dependency>
		<dependency>
			<groupId>commons-io</groupId>
			<artifactId>commons-io</artifactId>
			<version>2.6</version>
		</dependency>
		<dependency>
			<groupId>com.googlecode.json-simple</groupId>
			<artifactId>json-simple</artifactId>
			<version>1.1</version>
		</dependency>

2.路径处理:

遇到问题参考文章:https://blog.csdn.net/drose29/article/details/89351919

                                https://blog.csdn.net/weixin_40169609/article/details/107938090?utm_medium=distribute.pc_feed_404.none-task-blog-BlogCommendFromBaidu-1.nonecase&depth_1-utm_source=distribute.pc_feed_404.none-task-blog-BlogCommendFromBaidu-1.nonecas

做如下处理:

主要来处理后端程序(localhost:9191),允许前端extjs框架(localhost:1841)来访问上传的图片路径()。设置静态资源路径:

好像springboot默认以static文件夹为默认路径,但还是要在配置文件中设置一下:

filePath=D:\\workspace\\xianshangkuaidi\\api\\express-web\\src\\main\\resources\\static
spring.mvc.static-path-pattern=/**
spring.resources.static-locations=classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/,file:${filePath}

访问路径:http://localhost:9191/static

2.controller部分:


package com.gc.express.module.common.controller;

import com.alibaba.fastjson.JSONObject;
import org.apache.tomcat.util.http.fileupload.FileItem;
import org.apache.tomcat.util.http.fileupload.FileItemFactory;
import org.apache.tomcat.util.http.fileupload.FileUploadException;
import org.apache.tomcat.util.http.fileupload.RequestContext;
import org.apache.tomcat.util.http.fileupload.disk.DiskFileItemFactory;
import org.apache.tomcat.util.http.fileupload.servlet.ServletFileUpload;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Created by ldb on 2017/4/9.
 */
@Controller
@RestController
@CrossOrigin
public class KindEditorController {



       @ResponseBody
    @CrossOrigin(allowCredentials = "true")
    @RequestMapping("/KindEditorUpmethod")
    public JSONObject uploadMethod(@RequestParam String callBackPath, @RequestParam(value = "imgFile", required = false) MultipartFile file, HttpServletRequest request, HttpServletResponse response) throws FileUploadException, IOException {
        String scheme = request.getScheme();//http
        String serverName = request.getServerName();//localhost
//        int serverPort = request.getServerPort();//8080
//        String contextPath = request.getContextPath();//项目名
        //String url = scheme + "://" + serverName + ":" + serverPort + contextPath;//http://127.0.0.1:8080/test
        String url = scheme + "://" + serverName + ":9191";//http://127.0.0.1:8080

        String referer = request.getHeader("referer");
        Pattern p = Pattern.compile("([a-z]*:(//[^/?#]+/[^/?#]*/)?)", Pattern.CASE_INSENSITIVE);
        Matcher mathcer = p.matcher(referer);
        JSONObject msgMap = new JSONObject();

        if (mathcer.find()) {
            String htmlheader = mathcer.group();// 请求来源

            response.setContentType("text/html;charset=UTF-8");
            String savePath = "D://workspace/xianshangkuaidi/api/express-web/src/main/resources/static/";
            String saveUrl = request.getContextPath();
            //定义允许上传的文件扩展名
            HashMap<String, String> extMap = new HashMap<String, String>();
            extMap.put("image", "gif,jpg,jpeg,png,bmp");
            extMap.put("flash", "swf,flv");
            extMap.put("media", "swf,flv,mp3,wav,wma,wmv,mid,avi,mpg,asf,rm,rmvb");
            extMap.put("file", "doc,docx,xls,xlsx,ppt,htm,html,txt,zip,rar,gz,bz2");

            //最大文件大小
            long maxSize = 100000000;

            response.setContentType("text/html; charset=UTF-8");

            if (!ServletFileUpload.isMultipartContent(request)) {
                System.out.println("请选择文件。");
                response.sendRedirect(getError(htmlheader, "请选择文件.", callBackPath));
                return null;
            }
            //检查目录
            File uploadDir = new File(savePath);
            if (!uploadDir.isDirectory()) {
                System.out.println("上传目录不存在。");
                response.sendRedirect(getError(htmlheader, "上传目录不存在。", callBackPath));
                return null;
            }
            //检查目录写权限
            if (!uploadDir.canWrite()) {
                System.out.println("上传目录没有写权限。");
                response.sendRedirect(getError(htmlheader, "上传目录没有写权限。", callBackPath));
                return null;
            }

            String dirName = request.getParameter("dir");
            if (dirName == null) {
                dirName = "image";
            }
            if (!extMap.containsKey(dirName)) {
                System.out.println("目录名不正确。");
                response.sendRedirect(getError(htmlheader, "目录名不正确。", callBackPath));
                return null;
            }
            //创建文件夹
            savePath += dirName + "/";
            saveUrl += dirName + "/";
            File saveDirFile = new File(savePath);
            if (!saveDirFile.exists()) {
                saveDirFile.mkdirs();
            }
            SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
            String ymd = sdf.format(new Date());
            savePath += ymd + "/";
            saveUrl += ymd + "/";
            File dirFile = new File(savePath);
            if (!dirFile.exists()) {
                dirFile.mkdirs();
            }

            String fileName = file.getOriginalFilename();
            long fileSize = file.getSize();
//            if (!item.isFormField()) {
            //检查文件大小
            if (file.getSize() > maxSize) {
                System.out.println("上传文件大小超过限制。");
                response.sendRedirect(getError(htmlheader, "上传文件大小超过限制。", callBackPath));
                return null;
            }
            //检查扩展名
            String fileExt = fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase();
            if (!Arrays.<String>asList(extMap.get(dirName).split(",")).contains(fileExt)) {
                System.out.println("上传文件扩展名是不允许的扩展名。\n只允许" + extMap.get(dirName) + "格式。");
                response.sendRedirect(getError(htmlheader, "上传文件扩展名是不允许的扩展名。\n只允许" + extMap.get(dirName) + "格式。", callBackPath));
                return null;
            }

            SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");
            String newFileName = df.format(new Date()) + "_" + new Random().nextInt(1000) + "." + fileExt;
            try {
                File uploadedFile = new File(savePath, newFileName);
                OutputStream os = new FileOutputStream(uploadedFile);
                InputStream inputStream = file.getInputStream();
                byte[] buf = new byte[1024];
                int length = 0;
                while ((length = inputStream.read(buf)) != -1) {
                    os.write(buf, 0, length);
                }
                inputStream.close();
                os.close();
            } catch (Exception e) {
                System.out.println("上传文件失败。");
                response.sendRedirect(getError(htmlheader, "上传文件失败。", callBackPath));
                return null;
            }

            //Map<String, Object> msgMap = new HashMap<String, Object>();
            //遍历选择的图片
            msgMap.put("error", 0);
           // msgMap.put("url", "");
            msgMap.put("message", "");
            String urlString = "";
            //根据自己实际情况做修改
            //urlString = "http://localhost:63342/Beginner_admin/" + callBackPath + "?error=0&url=" + "http://192.168.2.158:8080/file/" + saveUrl + newFileName;
            //urlString = htmlheader + callBackPath + "?error=0&url=" + url+"/file/" + saveUrl + newFileName;
            urlString = callBackPath + "?error=0&url=" + url + "/" + saveUrl + newFileName;
            System.out.println(urlString);
            // response.sendRedirect(urlString);
            msgMap.put("url", url + "/" + saveUrl + newFileName);
            if (request.getParameter("single") != null && request.getParameter("single") != "" && "y".equals(request.getParameter("single"))) {
                //request.getParameter("single")   single :y 是在KindEditor 源码中个给单个图片上传添加的标志, 批量上传没有添加。
                //单个图片上传,需要跳转网址,用来支持跨域
                response.sendRedirect(urlString);
            } else {
                //多个图片上传,没有跳转网址,直接返回JSONObject数据,就可完成上传
                return msgMap;
            }
        }

        return null;
}

    private String getError(String htmluel, String message, String callBackPath) throws UnsupportedEncodingException {
        Map<String, Object> msg = new HashMap<String, Object>();
        msg.put("error", 1);
        msg.put("message", message);

        String urlString = htmluel + callBackPath + "?error=1&message=" + URLEncoder.encode(message, "UTF-8");

        return urlString;
    }


}



 

注意:

这段代码中增加了一段代码,用来区分是单个图片上传还是多个图片上传,single 参数是我自己在kindeditor-all.js源代码中增加的,用来区分单图和多图,多图没有添加参数。

            if (request.getParameter("single") != null && request.getParameter("single") != "" && "y".equals(request.getParameter("single"))) {
                //request.getParameter("single")   single :y 是在KindEditor 源码中个给单个图片上传添加的标志, 批量上传没有添加。
                //单个图片上传,需要跳转网址,用来支持跨域
                response.sendRedirect(urlString);
            } else {
                //多个图片上传,没有跳转网址,直接返回JSONObject数据,就可完成上传
                return msgMap;
            }

     

        这里很奇怪,        单个图片上传,需要跳转网址,用来支持跨域,多个图片上传,不需要跳转网址跨域,直接返回JSONObject数据,就可完成上传,困扰了很长时间。

kindeditor-all.js修改如下:

		var html = [
			'<div style="padding:20px;">',
			'<div class="tabs"></div>',
			'<div class="tab1" style="display:none;">',
			'<div class="ke-dialog-row">',
			'<label for="remoteUrl" style="width:60px;">' + lang.remoteUrl + '</label>',
			'<input type="text" id="remoteUrl" class="ke-input-text" name="url" value="" style="width:200px;" /> &nbsp;',
			'<span class="ke-button-common ke-button-outer">',
			'<input type="button" class="ke-button-common ke-button" name="viewServer" value="' + lang.viewServer + '" />',
			'</span>',
			'</div>',
			'<div class="ke-dialog-row">',
			'<label for="remoteWidth" style="width:60px;">' + lang.size + '</label>',
			lang.width + ' <input type="text" id="remoteWidth" class="ke-input-text ke-input-number" name="width" value="" maxlength="4" /> ',
			lang.height + ' <input type="text" class="ke-input-text ke-input-number" name="height" value="" maxlength="4" /> ',
			'<img class="ke-refresh-btn" src="' + imgPath + 'refresh.png" width="16" height="16" alt="" style="cursor:pointer;" title="' + lang.resetSize + '" />',
			'</div>',
			'<div class="ke-dialog-row">',
			'<label style="width:60px;">' + lang.align + '</label>',
			'<input type="radio" name="align" class="ke-inline-block" value="" checked="checked" /> <img name="defaultImg" src="' + imgPath + 'align_top.gif" width="23" height="25" alt="" />',
			' <input type="radio" name="align" class="ke-inline-block" value="left" /> <img name="leftImg" src="' + imgPath + 'align_left.gif" width="23" height="25" alt="" />',
			' <input type="radio" name="align" class="ke-inline-block" value="right" /> <img name="rightImg" src="' + imgPath + 'align_right.gif" width="23" height="25" alt="" />',
			'</div>',
			'<div class="ke-dialog-row">',
			'<label for="remoteTitle" style="width:60px;">' + lang.imgTitle + '</label>',
			'<input type="text" id="remoteTitle" class="ke-input-text" name="title" value="" style="width:200px;" />',
			'</div>',
			'</div>',
			'<div class="tab2" style="display:none;">',
			'<iframe name="' + target + '" style="display:none;"></iframe>',
			'<form class="ke-upload-area ke-form" method="post" enctype="multipart/form-data" target="' + target + '" action="' + K.addParam(uploadJson, 'dir=image&single=y') + '">',
			'<div class="ke-dialog-row">',
			hiddenElements.join(''),
			'<label style="width:60px;">' + lang.localUrl + '</label>',
			'<input type="text" name="localUrl" class="ke-input-text" tabindex="-1" style="width:200px;" readonly="true" /> &nbsp;',
			'<input type="button" class="ke-upload-button" value="' + lang.upload + '" />',
			'</div>',
			'</form>',
			'</div>',
			'</div>'
		].join('');

以上修改即完成。

还有一种方法单图和多图在配置时使用不同的url,这里只做备选项:

      

      kindeditor-all.js修改如下:

     

后台controller中也要相应修改,单图一个方法,多图一个方法。

 

到此为止,所有步骤基本完成。

三、测试

 

因为前后台分离,跨域问题几经周折,终于完成。

 

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值