springboot整合百度富文字編輯器ueditor實現圖片上傳和檔案上傳功能

靠譜楊發表於2022-06-16

springboot整合ueditor實現圖片上傳和檔案上傳功能

寫在前面:

在閱讀本篇之前,請先按照我的這篇隨筆完成對ueditor的前期配置工作:

springboot+layui 整合百度富文字編輯器ueditor入門使用教程(踩過的坑)https://www.cnblogs.com/rainbow-1/p/16365016.html

在完成對ueditor的基本配置後,圖片和檔案的上傳主要是後端檔案的配置,下面簡單介紹一下步驟。


一、修改ueditor.config.js配置檔案

首先第一步,要完成對配置檔案的修改。預設的ueditor配置裡,對配置檔案的讀取是通過controller.jsp完成的,

因為springboot專案中,靜態jsp檔案的訪問是不被允許的,所以我們需要重寫這段讀取配置檔案資訊的程式碼,也就是通過寫自己的一個controller來完成這個過程,同時自定義圖片和檔案上傳的位置等資訊。

預設的controller.jsp位置如下圖:

這時候我們修改配置檔案呼叫資源的介面名稱,開啟ueditor.config.js,如下圖所示:

我通過server_url,拿到了專案的根路徑,然後拼接後面的/science-2.0/api/ueditor/config(專案名稱/路由名稱)拿到完整的訪問路徑。

二、編寫一個新的控制器(CommonController)

這個類就是我們第一步所修改的部分指向的這個控制器,它要完成對config.json檔案(該檔案位於jsp資料夾下,裡面是關於檔案上傳的一些配置資訊)的讀取,同時要完成對檔案上傳的具體實現。

  • 下面我們先來看一下config.json檔案(/jsp目錄下)

​ 主要看一下圖片上傳配置的部分:

我們需要修改的部分如圖所示:basePath指的是你希望儲存的磁碟位置(這個目錄最好事先存在),imageActionName是我們處理上傳圖片功能的那個路由,也就是在controller裡是什麼,這裡就是什麼。

下面的檔案上傳部分類似:

  • 下面給出完成的controller程式碼
import com.alibaba.fastjson.JSONException;
import com.baidu.ueditor.ActionEnter;
import com.google.common.base.Charsets;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.util.ClassUtils;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLDecoder;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.net.URL;
@RestController
@RequestMapping("/api/ueditor")
public class CommonController {
    /**
     * 判斷當前系統是否是Windows系統
     * @return true:Windows系統,false:Linux系統
     */
    private boolean isWindowsSystem(){
        String property = System.getProperty("os.name").toLowerCase();
        return property.contains("windows");
    }

    /**
     * 獲取Ueditor的配置檔案
     * @return
     */
    @RequestMapping("/config")
    public void getConfigInfo(HttpServletRequest request,HttpServletResponse response) throws UnsupportedEncodingException {
        System.out.println("讀取ueditor配置檔案!");
        response.setContentType("application/json");
        String rootPath = "";
        // 判斷當前系統是否是Windows系統
        if(isWindowsSystem()){
            URL Path = ClassUtils.getDefaultClassLoader().getResource("");
            String afterPath = URLDecoder.decode(Path.getPath(), "UTF-8");
            rootPath = afterPath+ "static/UEditor/jsp";
        } else {
            // 將config.json檔案放在jar包同級目錄下
            rootPath = "/usr/local/zgxsoft/yunapp-backend/service";
        }
        System.out.println("rootPath:{}"+ rootPath);
        try {
            String exec = new ActionEnter(request, rootPath, "/config.json").exec();
            PrintWriter writer = response.getWriter();
            writer.write(exec);
            writer.flush();
            writer.close();
        } catch (IOException | JSONException e) {
            e.printStackTrace();
        }
    }


    /**
     * Ueditor上傳檔案
     * 這裡以上傳圖片為例,圖片上傳後,imgPath將儲存圖片的儲存路徑,返回到編輯器中做展示
     * @param upfile
     * @return
     */

    @RequestMapping("uploadimage")
    @ResponseBody
    public Map<String,String> uploadImage(@RequestParam("upfile") MultipartFile upfile, HttpServletRequest request) throws IOException {
        System.out.println("上傳圖片!");
        //檔案原名稱
        String fileName = upfile.getOriginalFilename();

        // 儲存檔案的新名字

        String timeFileName = DateHelper.getDateToString(new Date());
        String nowName = timeFileName+"_"+UUID.randomUUID()+fileName.substring(upfile.getOriginalFilename().lastIndexOf("."));
        String uploadPath = "";
        if(!upfile.isEmpty()){
            String path = "D:/science-2.0/";
            File f = new File(path);
            if(!f.exists()){
                // 第一次上傳檔案新建資料夾
                f.mkdirs();
            }
            uploadPath = path+nowName;
            //按照路徑新建檔案
            File newFile = new File(uploadPath);
            if(!newFile.exists()){
                newFile.createNewFile();
            }
            //複製
            FileCopyUtils.copy(upfile.getBytes(), newFile);
        }
        //返回結果資訊(UEditor官方要求這個json格式)
        Map<String,String> map = new HashMap<String,String >();
        //是否上傳成功
        map.put("state", "SUCCESS");
        //現在檔名稱
        map.put("title", nowName);
        //檔案原名稱
        map.put("original", fileName);
        //檔案型別 .+字尾名
        map.put("type", fileName.substring(upfile.getOriginalFilename().lastIndexOf(".")));
        //檔案路徑
        // map.put("url", uploadPath);    // 瀏覽器不能直接訪問專案外目錄的圖片等檔案,需要做虛擬路徑對映
        map.put("url", "/PathImage/"+nowName);  // 這個路徑的 /PathImage/ 是在配置類裡指定的對映到本地的絕對路徑
        //檔案大小(位元組數)
        map.put("size", upfile.getSize()+"");
        return map;
    }

    @RequestMapping("uploadfile")
    @ResponseBody
    public Map<String,String> uploadfile(@RequestParam("upfile") MultipartFile upfile, HttpServletRequest request) throws IOException {
        System.out.println("上傳檔案!");
        //檔案原名稱
        String fileName = upfile.getOriginalFilename();

        // 儲存檔案的新名字

        String timeFileName = DateHelper.getDateToString(new Date());
        String nowName = timeFileName+"_"+UUID.randomUUID()+fileName.substring(upfile.getOriginalFilename().lastIndexOf("."));
        //System.out.println("name---:"+nowName);
        String uploadPath = "";
        if(!upfile.isEmpty()){
            String path = "D:/science-2.0/";
            File f = new File(path);
            if(!f.exists()){
                // 第一次上傳檔案新建資料夾
                f.mkdirs();
            }
            uploadPath = path+nowName;
            //按照路徑新建檔案
            File newFile = new File(uploadPath);
            if(!newFile.exists()){
                newFile.createNewFile();
            }
            //複製
            FileCopyUtils.copy(upfile.getBytes(), newFile);
        }
        //返回結果資訊(UEditor官方要求這個json格式)
        Map<String,String> map = new HashMap<String,String >();
        //是否上傳成功
        map.put("state", "SUCCESS");
        //現在檔名稱
        map.put("title", nowName);
        //檔案原名稱
        map.put("original", fileName);
        //檔案型別 .+字尾名
        map.put("type", fileName.substring(upfile.getOriginalFilename().lastIndexOf(".")));
        //檔案路徑
        // map.put("url", uploadPath);    // 瀏覽器不能直接訪問專案外目錄的圖片等檔案,需要做虛擬路徑對映
        map.put("url", "/PathFile/"+nowName);  // 這個路徑的 /PathImage/ 是在配置類裡指定的對映到本地的絕對路徑
        //檔案大小(位元組數)
        map.put("size", upfile.getSize()+"");
        return map;
    }
}

這部分程式碼裡面的內容簡單說一下:

  1. /api/ueditor/config,這個路徑是我們讀取config.json檔案的地方,在這裡我們完成了對json檔案的載入,這時候我們在點選控制元件上的上傳按鈕才可以被監聽到,那麼我們後面重寫的圖片和檔案上傳的路徑才有了意義。
  2. /api/ueditor/uploadimage,這個路徑就是寫圖片上傳的部分,這部分程式碼註釋比較詳細,讀者可以仔細閱讀。
  3. /api/ueditor/uploadfile,這個就是檔案上傳的部分,和圖片類似(圖片實際上也是屬於檔案範疇)

!!!!!!

劃重點:

下面還需要完成一個重要的配置,我們目前完成的只是對檔案的上傳儲存到磁碟的功能,但是我們還有一件事也很重要,那就是回顯,所謂回顯,就是把圖片和檔案等對前端提供展示。那麼這部分要怎麼寫呢,其實道理很簡單,就是對本地磁碟的檔案提供一個訪問的路徑就可以了。

我們回到最開始的話題,我們現在做的事情其實是放棄了控制元件原有的controller,而自己寫了一個新的,這是因為springboot框架的訪問限制,那麼圖片和檔案的回顯也就需要我們自己寫不可以直接用config.json中的預設配置,因為那裡其實已經不能用了,我們要重寫就得重寫得徹徹底底。

預設是這樣的:

"imageUrlPrefix":"", /* 圖片訪問路徑字首 */
"imagePathFormat": "/document/ueditor/image/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上傳儲存路徑,可以自定義儲存路徑和檔名格式 */
                            /* {filename} 會替換成原檔名,配置這項需要注意中文亂碼問題 */
                            /* {rand:6} 會替換成隨機數,後面的數字是隨機數的位數 */
                            /* {time} 會替換成時間戳 */
                            /* {yyyy} 會替換成四位年份 */
                            /* {yy} 會替換成兩位年份 */
                            /* {mm} 會替換成兩位月份 */
                            /* {dd} 會替換成兩位日期 */
                            /* {hh} 會替換成兩位小時 */
                            /* {ii} 會替換成兩位分鐘 */
                            /* {ss} 會替換成兩位秒 */
                            /* 非法字元 \ : * ? " < > | */
                            /* 具請體看線上文件: fex.baidu.com/ueditor/#use-format_upload_filename */
    

ueditor為我們提供了重寫之後需要給伺服器返回資料的格式:

{
    "state": "SUCCESS",
    "url": "upload/demo.jpg",
    "title": "demo.jpg",
    "original": "demo.jpg"
}
//這是官網提供的格式,我們還可以再加一些,比如上面的controller是這麼寫的:
//返回結果資訊(UEditor官方要求這個json格式)
        Map<String,String> map = new HashMap<String,String >();
        //是否上傳成功
        map.put("state", "SUCCESS");
        //現在檔名稱
        map.put("title", nowName);
        //檔案原名稱
        map.put("original", fileName);
        //檔案型別 .+字尾名
        map.put("type", fileName.substring(upfile.getOriginalFilename().lastIndexOf(".")));
        //檔案路徑
        // map.put("url", uploadPath);    // 瀏覽器不能直接訪問專案外目錄的圖片等檔案,需要做虛擬路徑對映
        map.put("url", "/PathFile/"+nowName);  // 這個路徑的 /PathImage/ 是在配置類裡指定的對映到本地的絕對路徑
        //檔案大小(位元組數)
        map.put("size", upfile.getSize()+"");

注意看map.put("url", "/PathFile/"+nowName);這段程式碼

這段程式碼完成的就是對應本地檔案路徑的伺服器訪問路徑,所以我們需要再寫一個對映器檔案完成對這個路徑的對映關係。

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
@Configuration
public class MyWebAppConfiguration extends WebMvcConfigurerAdapter {
    /**
     * Spring Boot中有預設的靜態資源訪問路徑,瀏覽器也不允許訪問專案目錄外的資原始檔
     * 新增一些虛擬路徑的對映
     * 設定靜態資源路徑和上傳檔案的路徑
     */
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        // /** 表示該目錄下所有檔案
        registry.addResourceHandler("/PathImage/**").addResourceLocations("file:/D:/science-2.0/");
        registry.addResourceHandler("/PathFile/**").addResourceLocations("file:/D:/science-2.0/");
        super.addResourceHandlers(registry);
    }
}

其中由於檔名的日期字尾是根據當前系統時間生成的,所以又封裝了一個日期類,程式碼如下:

import java.text.SimpleDateFormat;
import java.util.Date;

public class DateHelper {
    public static String getDateToString(Date time) {
        SimpleDateFormat formatter;
        formatter = new SimpleDateFormat("yyyyMMddHHmmss");
        String Time = formatter.format(time);
        return Time;
    }
}

小結一下

這裡我們主要在寫後端的程式碼,我們一共需要三個類:一個是主控制器,一個是對映關係類,還有一個是日期處理類(這三個檔案放一起就行)。注意裡面的一些關鍵路徑,和config.json裡的路徑是一一對應的,一旦寫錯伺服器是無法訪問到的。還有對映關係類裡的路徑也需要寫正確,否則伺服器只能完成對檔案的上傳而無法完成對檔案的回顯。


附ueditor.config.js

/**
 * ueditor完整配置項
 * 可以在這裡配置整個編輯器的特性
 */
/**************************提示********************************
 * 所有被註釋的配置項均為UEditor預設值。
 * 修改預設配置請首先確保已經完全明確該引數的真實用途。
 * 主要有兩種修改方案,一種是取消此處註釋,然後修改成對應引數;另一種是在例項化編輯器時傳入對應引數。
 * 當升級編輯器時,可直接使用舊版配置檔案替換新版配置檔案,不用擔心舊版配置檔案中因缺少新功能所需的引數而導致指令碼報錯。
 **************************提示********************************/

(function () {

    /**
     * 編輯器資原始檔根路徑。它所表示的含義是:以編輯器例項化頁面為當前路徑,指向編輯器資原始檔(即dialog等資料夾)的路徑。
     * 鑑於很多同學在使用編輯器的時候出現的種種路徑問題,此處強烈建議大家使用"相對於網站根目錄的相對路徑"進行配置。
     * "相對於網站根目錄的相對路徑"也就是以斜槓開頭的形如"/myProject/ueditor/"這樣的路徑。
     * 如果站點中有多個不在同一層級的頁面需要例項化編輯器,且引用了同一UEditor的時候,此處的URL可能不適用於每個頁面的編輯器。
     * 因此,UEditor提供了針對不同頁面的編輯器可單獨配置的根路徑,具體來說,在需要例項化編輯器的頁面最頂部寫上如下程式碼即可。
     * 當然,需要令此處的URL等於對應的配置。
     * window.UEDITOR_HOME_URL = "/xxxx/xxxx/";
     */
   window.UEDITOR_HOME_URL = "/UEditor/";


    var server_url = window.location.protocol+"//"+window.location.hostname+":"+window.location.port;
    //alert("rul"+server_url);
    /**
     * 配置項主體。注意,此處所有涉及到路徑的配置別遺漏URL變數。
     */
    var ser_url=server_url+"/api/ueditor/config";
    var ip=window.location.hostname;
    if(ip=="10.1.10.148"){
        window.UEDITOR_HOME_URL = "/science-2.0/UEditor/";
        ser_url= server_url+"/science-2.0/api/ueditor/config";
    }
    var URL = window.UEDITOR_HOME_URL || getUEBasePath();
    window.UEDITOR_CONFIG = {

        //為編輯器例項新增一個路徑,這個不能被註釋
        UEDITOR_HOME_URL: URL

        // 伺服器統一請求介面路徑
        , serverUrl:  ser_url

        //工具欄上的所有的功能按鈕和下拉框,可以在new編輯器的例項時選擇自己需要的重新定義
        , toolbars: [[
            'undo', 'redo', '|', 'bold', 'italic', 
            'underline', 'fontborder', 'strikethrough', 'superscript', 'subscript', 
            'removeformat', 'formatmatch', 'autotypeset', 'blockquote', 'pasteplain', 
            '|', 'forecolor', 'backcolor', 'insertorderedlist', 'insertunorderedlist', 
            'selectall', 'cleardoc', '|', 'rowspacingtop', 'rowspacingbottom', 'lineheight', 
            '|', 'customstyle', 'paragraph', 'fontfamily', 'fontsize', '|', 'directionalityltr', 
            'directionalityrtl', 'indent', '|', 'justifyleft', 'justifycenter', 'justifyright',
            'justifyjustify', '|', 'touppercase', 'tolowercase', '|','link', 'unlink', 'anchor', 
            '|', 'imagenone', 'imageleft', 'imageright', 'imagecenter', '|', 'simpleupload',
            'insertimage', 'emotion', 'scrawl', 'insertvideo', 'music', 'attachment', 'map', 
            'pagebreak', 'template','|','horizontal', 'date', 'time', 'spechars', '|',
            'inserttable', 'deletetable', 'insertparagraphbeforetable', 'insertrow', 'deleterow',
            'insertcol', 'deletecol', 'mergecells', 'mergeright', 'mergedown', 'splittocells',
            'splittorows', 'splittocols', 'charts', '|', 'preview', 'searchreplace'
        ]]
        //當滑鼠放在工具欄上時顯示的tooltip提示,留空支援自動多語言配置,否則以配置值為準
        //,labelMap:{
        //    'anchor':'', 'undo':''
        //}

        //語言配置項,預設是zh-cn。有需要的話也可以使用如下這樣的方式來自動多語言切換,當然,前提條件是lang資料夾下存在對應的語言檔案:
        //lang值也可以通過自動獲取 (navigator.language||navigator.browserLanguage ||navigator.userLanguage).toLowerCase()
        //,lang:"zh-cn"
        //,langPath:URL +"lang/"

        //主題配置項,預設是default。有需要的話也可以使用如下這樣的方式來自動多主題切換,當然,前提條件是themes資料夾下存在對應的主題檔案:
        //現有如下皮膚:default
        //,theme:'default'
        //,themePath:URL +"themes/"

        //,zIndex : 900     //編輯器層級的基數,預設是900

        //針對getAllHtml方法,會在對應的head標籤中增加該編碼設定。
        //,charset:"utf-8"

        //若例項化編輯器的頁面手動修改的domain,此處需要設定為true
        //,customDomain:false

        //常用配置專案
        //,isShow : true    //預設顯示編輯器

        //,textarea:'editorValue' // 提交表單時,伺服器獲取編輯器提交內容的所用的引數,多例項時可以給容器name屬性,會將name給定的值最為每個例項的鍵值,不用每次例項化的時候都設定這個值

        //,initialContent:'歡迎使用ueditor!'    //初始化編輯器的內容,也可以通過textarea/script給值,看官網例子

        //,autoClearinitialContent:true //是否自動清除編輯器初始內容,注意:如果focus屬性設定為true,這個也為真,那麼編輯器一上來就會觸發導致初始化的內容看不到了

        //,focus:false //初始化時,是否讓編輯器獲得焦點true或false

        //如果自定義,最好給p標籤如下的行高,要不輸入中文時,會有跳動感
        //,initialStyle:'p{line-height:1em}'//編輯器層級的基數,可以用來改變字型等

        //,iframeCssUrl: URL + '/themes/iframe.css' //給編輯區域的iframe引入一個css檔案

        //indentValue
        //首行縮排距離,預設是2em
        //,indentValue:'2em'

        //,initialFrameWidth:1000  //初始化編輯器寬度,預設1000
        //,initialFrameHeight:320  //初始化編輯器高度,預設320

        //,readonly : false //編輯器初始化結束後,編輯區域是否是隻讀的,預設是false

        //,autoClearEmptyNode : true //getContent時,是否刪除空的inlineElement節點(包括巢狀的情況)

        //啟用自動儲存
        //,enableAutoSave: true
        //自動儲存間隔時間, 單位ms
        //,saveInterval: 500

        //,fullscreen : false //是否開啟初始化時即全屏,預設關閉

        //,imagePopup:true      //圖片操作的浮層開關,預設開啟

        //,autoSyncData:true //自動同步編輯器要提交的資料
        //,emotionLocalization:false //是否開啟表情本地化,預設關閉。若要開啟請確保emotion資料夾下包含官網提供的images表情資料夾

        //貼上只保留標籤,去除標籤所有屬性
        //,retainOnlyLabelPasted: false

        //,pasteplain:false  //是否預設為純文字貼上。false為不使用純文字貼上,true為使用純文字貼上
        //純文字貼上模式下的過濾規則
        //'filterTxtRules' : function(){
        //    function transP(node){
        //        node.tagName = 'p';
        //        node.setStyle();
        //    }
        //    return {
        //        //直接刪除及其位元組點內容
        //        '-' : 'script style object iframe embed input select',
        //        'p': {$:{}},
        //        'br':{$:{}},
        //        'div':{'$':{}},
        //        'li':{'$':{}},
        //        'caption':transP,
        //        'th':transP,
        //        'tr':transP,
        //        'h1':transP,'h2':transP,'h3':transP,'h4':transP,'h5':transP,'h6':transP,
        //        'td':function(node){
        //            //沒有內容的td直接刪掉
        //            var txt = !!node.innerText();
        //            if(txt){
        //                node.parentNode.insertAfter(UE.uNode.createText(' &nbsp; &nbsp;'),node);
        //            }
        //            node.parentNode.removeChild(node,node.innerText())
        //        }
        //    }
        //}()

        //,allHtmlEnabled:false //提交到後臺的資料是否包含整個html字串

        //insertorderedlist
        //有序列表的下拉配置,值留空時支援多語言自動識別,若配置值,則以此值為準
        //,'insertorderedlist':{
        //      //自定的樣式
        //        'num':'1,2,3...',
        //        'num1':'1),2),3)...',
        //        'num2':'(1),(2),(3)...',
        //        'cn':'一,二,三....',
        //        'cn1':'一),二),三)....',
        //        'cn2':'(一),(二),(三)....',
        //     //系統自帶
        //     'decimal' : '' ,         //'1,2,3...'
        //     'lower-alpha' : '' ,    // 'a,b,c...'
        //     'lower-roman' : '' ,    //'i,ii,iii...'
        //     'upper-alpha' : '' , lang   //'A,B,C'
        //     'upper-roman' : ''      //'I,II,III...'
        //}

        //insertunorderedlist
        //無序列表的下拉配置,值留空時支援多語言自動識別,若配置值,則以此值為準
        //,insertunorderedlist : { //自定的樣式
        //    'dash' :'— 破折號', //-破折號
        //    'dot':' 。 小圓圈', //系統自帶
        //    'circle' : '',  // '○ 小圓圈'
        //    'disc' : '',    // '● 小圓點'
        //    'square' : ''   //'■ 小方塊'
        //}
        //,listDefaultPaddingLeft : '30'//預設的左邊縮排的基數倍
        //,listiconpath : 'http://bs.baidu.com/listicon/'//自定義標號的路徑
        //,maxListLevel : 3 //限制可以tab的級數, 設定-1為不限制

        //,autoTransWordToList:false  //禁止word中貼上進來的列表自動變成列表標籤

        //fontfamily
        //字型設定 label留空支援多語言自動切換,若配置,則以配置值為準
        ,'fontfamily':[
            { label:'',name:'songti',val:'宋體,SimSun'},
            { label: '', name: 'SimSun', val: '仿宋,SimSun' },
            { label:'',name:'FangSong_GB2312',val:'仿宋_GB2312 ,FangSong_GB2312'},
            { label:'',name:'kaiti',val:'楷體,楷體_GB2312, SimKai'},
            { label:'',name:'yahei',val:'微軟雅黑,Microsoft YaHei'},
            { label:'',name:'heiti',val:'黑體, SimHei'},
            { label:'',name:'lishu',val:'隸書, SimLi'},
            { label:'',name:'andaleMono',val:'andale mono'},
            { label:'',name:'arial',val:'arial, helvetica,sans-serif'},
            { label:'',name:'arialBlack',val:'arial black,avant garde'},
            { label:'',name:'comicSansMs',val:'comic sans ms'},
            { label:'',name:'impact',val:'impact,chicago'},
            { label:'',name:'timesNewRoman',val:'times new roman'}
        ]

        //fontsize
        //字號
        //,'fontsize':[10, 11, 12, 14, 16, 18, 20, 24, 36]

        //paragraph
        //段落格式 值留空時支援多語言自動識別,若配置,則以配置值為準
        //,'paragraph':{'p':'', 'h1':'', 'h2':'', 'h3':'', 'h4':'', 'h5':'', 'h6':''}

        //rowspacingtop
        //段間距 值和顯示的名字相同
        //,'rowspacingtop':['5', '10', '15', '20', '25']

        //rowspacingBottom
        //段間距 值和顯示的名字相同
        //,'rowspacingbottom':['5', '10', '15', '20', '25']

        //lineheight
        //行內間距 值和顯示的名字相同
        //,'lineheight':['1', '1.5','1.75','2', '3', '4', '5']

        //customstyle
        //自定義樣式,不支援國際化,此處配置值即可最後顯示值
        //block的元素是依據設定段落的邏輯設定的,inline的元素依據BIU的邏輯設定
        //儘量使用一些常用的標籤
        //引數說明
        //tag 使用的標籤名字
        //label 顯示的名字也是用來標識不同型別的識別符號,注意這個值每個要不同,
        //style 新增的樣式
        //每一個物件就是一個自定義的樣式
        //,'customstyle':[
        //    {tag:'h1', name:'tc', label:'', style:'border-bottom:#ccc 2px solid;padding:0 4px 0 0;text-align:center;margin:0 0 20px 0;'},
        //    {tag:'h1', name:'tl',label:'', style:'border-bottom:#ccc 2px solid;padding:0 4px 0 0;margin:0 0 10px 0;'},
        //    {tag:'span',name:'im', label:'', style:'font-style:italic;font-weight:bold'},
        //    {tag:'span',name:'hi', label:'', style:'font-style:italic;font-weight:bold;color:rgb(51, 153, 204)'}
        //]

        //開啟右鍵選單功能
        //,enableContextMenu: true
        //右鍵選單的內容,可以參考plugins/contextmenu.js裡邊的預設選單的例子,label留空支援國際化,否則以此配置為準
        //,contextMenu:[
        //    {
        //        label:'',       //顯示的名稱
        //        cmdName:'selectall',//執行的command命令,當點選這個右鍵選單時
        //        //exec可選,有了exec就會在點選時執行這個function,優先順序高於cmdName
        //        exec:function () {
        //            //this是當前編輯器的例項
        //            //this.ui._dialogs['inserttableDialog'].open();
        //        }
        //    }
        //]

        //快捷選單
        //,shortcutMenu:["fontfamily", "fontsize", "bold", "italic", "underline", "forecolor", "backcolor", "insertorderedlist", "insertunorderedlist"]

        //elementPathEnabled
        //是否啟用元素路徑,預設是顯示
        //,elementPathEnabled : true

        //wordCount
        //,wordCount:true          //是否開啟字數統計
        //,maximumWords:10000       //允許的最大字元數
        //字數統計提示,{#count}代表當前字數,{#leave}代表還可以輸入多少字元數,留空支援多語言自動切換,否則按此配置顯示
        //,wordCountMsg:''   //當前已輸入 {#count} 個字元,您還可以輸入{#leave} 個字元
        //超出字數限制提示  留空支援多語言自動切換,否則按此配置顯示
        //,wordOverFlowMsg:''    //<span style="color:red;">你輸入的字元個數已經超出最大允許值,伺服器可能會拒絕儲存!</span>

        //tab
        //點選tab鍵時移動的距離,tabSize倍數,tabNode什麼字元做為單位
        //,tabSize:4
        //,tabNode:'&nbsp;'

        //removeFormat
        //清除格式時可以刪除的標籤和屬性
        //removeForamtTags標籤
        //,removeFormatTags:'b,big,code,del,dfn,em,font,i,ins,kbd,q,samp,small,span,strike,strong,sub,sup,tt,u,var'
        //removeFormatAttributes屬性
        //,removeFormatAttributes:'class,style,lang,width,height,align,hspace,valign'

        //undo
        //可以最多回退的次數,預設20
        //,maxUndoCount:20
        //當輸入的字元數超過該值時,儲存一次現場
        //,maxInputCount:1

        //autoHeightEnabled
        // 是否自動長高,預設true
        //,autoHeightEnabled:true

        //scaleEnabled
        //是否可以拉伸長高,預設true(當開啟時,自動長高失效)
        //,scaleEnabled:false
        //,minFrameWidth:800    //編輯器拖動時最小寬度,預設800
        //,minFrameHeight:220  //編輯器拖動時最小高度,預設220

        //autoFloatEnabled
        //是否保持toolbar的位置不動,預設true
        //,autoFloatEnabled:true
        //浮動時工具欄距離瀏覽器頂部的高度,用於某些具有固定頭部的頁面
        //,topOffset:30
        //編輯器底部距離工具欄高度(如果引數大於等於編輯器高度,則設定無效)
        //,toolbarTopOffset:400

        //設定遠端圖片是否抓取到本地儲存
        //,catchRemoteImageEnable: true //設定是否抓取遠端圖片

        //pageBreakTag
        //分頁識別符號,預設是_ueditor_page_break_tag_
        //,pageBreakTag:'_ueditor_page_break_tag_'

        //autotypeset
        //自動排版引數
        //,autotypeset: {
        //    mergeEmptyline: true,           //合併空行
        //    removeClass: true,              //去掉冗餘的class
        //    removeEmptyline: false,         //去掉空行
        //    textAlign:"left",               //段落的排版方式,可以是 left,right,center,justify 去掉這個屬性表示不執行排版
        //    imageBlockLine: 'center',       //圖片的浮動方式,獨佔一行劇中,左右浮動,預設: center,left,right,none 去掉這個屬性表示不執行排版
        //    pasteFilter: false,             //根據規則過濾沒事貼上進來的內容
        //    clearFontSize: false,           //去掉所有的內嵌字號,使用編輯器預設的字號
        //    clearFontFamily: false,         //去掉所有的內嵌字型,使用編輯器預設的字型
        //    removeEmptyNode: false,         // 去掉空節點
        //    //可以去掉的標籤
        //    removeTagNames: {標籤名字:1},
        //    indent: false,                  // 行首縮排
        //    indentValue : '2em',            //行首縮排的大小
        //    bdc2sb: false,
        //    tobdc: false
        //}

        //tableDragable
        //表格是否可以拖拽
        //,tableDragable: true



        //sourceEditor
        //原始碼的檢視方式,codemirror 是程式碼高亮,textarea是文字框,預設是codemirror
        //注意預設codemirror只能在ie8+和非ie中使用
        //,sourceEditor:"codemirror"
        //如果sourceEditor是codemirror,還用配置一下兩個引數
        //codeMirrorJsUrl js載入的路徑,預設是 URL + "third-party/codemirror/codemirror.js"
        //,codeMirrorJsUrl:URL + "third-party/codemirror/codemirror.js"
        //codeMirrorCssUrl css載入的路徑,預設是 URL + "third-party/codemirror/codemirror.css"
        //,codeMirrorCssUrl:URL + "third-party/codemirror/codemirror.css"
        //編輯器初始化完成後是否進入原始碼模式,預設為否。
        //,sourceEditorFirst:false

        //iframeUrlMap
        //dialog內容的路徑 ~會被替換成URL,垓屬性一旦開啟,將覆蓋所有的dialog的預設路徑
        //,iframeUrlMap:{
        //    'anchor':'~/dialogs/anchor/anchor.html',
        //}

        //allowLinkProtocol 允許的連結地址,有這些字首的連結地址不會自動新增http
        //, allowLinkProtocols: ['http:', 'https:', '#', '/', 'ftp:', 'mailto:', 'tel:', 'git:', 'svn:']

        //webAppKey 百度應用的APIkey,每個站長必須首先去百度官網註冊一個key後方能正常使用app功能,註冊介紹,http://app.baidu.com/static/cms/getapikey.html
        //, webAppKey: ""

        //預設過濾規則相關配置專案
        //,disabledTableInTable:true  //禁止表格巢狀
        //,allowDivTransToP:true      //允許進入編輯器的div標籤自動變成p標籤
        //,rgb2Hex:true               //預設產出的資料中的color自動從rgb格式變成16進位制格式

      // xss 過濾是否開啟,inserthtml等操作
      ,xssFilterRules: true
      //input xss過濾
      ,inputXssFilter: true
      //output xss過濾
      ,outputXssFilter: true
      // xss過濾白名單 名單來源: https://raw.githubusercontent.com/leizongmin/js-xss/master/lib/default.js
      ,whitList: {
         iframe: ['frameborder','border','marginwidth','marginheight','width','height','src','id'],//宋雨佳增加這一行
         a:      ['target', 'href', 'title', 'class', 'style'],
         abbr:   ['title', 'class', 'style'],
         address: ['class', 'style'],
         area:   ['shape', 'coords', 'href', 'alt'],
         article: [],
         aside:  [],
         audio:  ['autoplay', 'controls', 'loop', 'preload', 'src', 'class', 'style'],
         b:      ['class', 'style'],
         bdi:    ['dir'],
         bdo:    ['dir'],
         big:    [],
         blockquote: ['cite', 'class', 'style'],
         br:     [],
         caption: ['class', 'style'],
         center: [],
         cite:   [],
         code:   ['class', 'style'],
         col:    ['align', 'valign', 'span', 'width', 'class', 'style'],
         colgroup: ['align', 'valign', 'span', 'width', 'class', 'style'],
         dd:     ['class', 'style'],
         del:    ['datetime'],
         details: ['open'],
         div:    ['class', 'style'],
         dl:     ['class', 'style'],
         dt:     ['class', 'style'],
         em:     ['class', 'style'],
         font:   ['color', 'size', 'face'],
         footer: [],
         h1:     ['class', 'style'],
         h2:     ['class', 'style'],
         h3:     ['class', 'style'],
         h4:     ['class', 'style'],
         h5:     ['class', 'style'],
         h6:     ['class', 'style'],
         header: [],
         hr:     [],
         i:      ['class', 'style'],
         img:    ['src', 'alt', 'title', 'width', 'height', 'id', '_src', 'loadingclass', 'class', 'data-latex'],
         ins:    ['datetime'],
         li:     ['class', 'style'],
         mark:   [],
         nav:    [],
         ol:     ['class', 'style'],
         p:      ['class', 'style'],
         pre:    ['class', 'style'],
         s:      [],
         section:[],
         small:  [],
         span:   ['class', 'style'],
         sub:    ['class', 'style'],
         sup:    ['class', 'style'],
         strong: ['class', 'style'],
         table:  ['width', 'border', 'align', 'valign', 'class', 'style'],
         tbody:  ['align', 'valign', 'class', 'style'],
         td:     ['width', 'rowspan', 'colspan', 'align', 'valign', 'class', 'style'],
         tfoot:  ['align', 'valign', 'class', 'style'],
         th:     ['width', 'rowspan', 'colspan', 'align', 'valign', 'class', 'style'],
         thead:  ['align', 'valign', 'class', 'style'],
         tr:     ['rowspan', 'align', 'valign', 'class', 'style'],
         tt:     [],
         u:      [],
         ul:     ['class', 'style'],
         video:  ['autoplay', 'controls', 'loop', 'preload', 'src', 'height', 'width', 'class', 'style']
      }
    };

    function getUEBasePath(docUrl, confUrl) {

        return getBasePath(docUrl || self.document.URL || self.location.href, confUrl || getConfigFilePath());

    }

    function getConfigFilePath() {

        var configPath = document.getElementsByTagName('script');

        return configPath[ configPath.length - 1 ].src;

    }

    function getBasePath(docUrl, confUrl) {

        var basePath = confUrl;


        if (/^(\/|\\\\)/.test(confUrl)) {

            basePath = /^.+?\w(\/|\\\\)/.exec(docUrl)[0] + confUrl.replace(/^(\/|\\\\)/, '');

        } else if (!/^[a-z]+:/i.test(confUrl)) {

            docUrl = docUrl.split("#")[0].split("?")[0].replace(/[^\\\/]+$/, '');

            basePath = docUrl + "" + confUrl;

        }

        return optimizationPath(basePath);

    }

    function optimizationPath(path) {

        var protocol = /^[a-z]+:\/\//.exec(path)[ 0 ],
            tmp = null,
            res = [];

        path = path.replace(protocol, "").split("?")[0].split("#")[0];

        path = path.replace(/\\/g, '/').split(/\//);

        path[ path.length - 1 ] = "";

        while (path.length) {

            if (( tmp = path.shift() ) === "..") {
                res.pop();
            } else if (tmp !== ".") {
                res.push(tmp);
            }

        }

        return protocol + res.join("/");

    }

    window.UE = {
        getUEBasePath: getUEBasePath
    };

})();

附config.json

/* 前後端通訊相關的配置,註釋只允許使用多行方式 */
{
    "basePath": "D:/scinece-2.0",
    /* 上傳圖片配置項 */
    "imageActionName": "uploadimage", /* 執行上傳圖片的action名稱 */
    "imageFieldName": "upfile", /* 提交的圖片表單名稱 */
    "imageMaxSize": 2048000, /* 上傳大小限制,單位B  */
    "imageAllowFiles": [".png", ".jpg", ".jpeg", ".gif", ".bmp"], /* 上傳圖片格式顯示 */
    "imageCompressEnable": true, /* 是否壓縮圖片,預設是true */
    "imageCompressBorder": 1600, /* 圖片壓縮最長邊限制 */
    "imageInsertAlign": "none", /* 插入的圖片浮動方式 */
    "imageUrlPrefix":"", /* 圖片訪問路徑字首 */
    "imagePathFormat": "/document/ueditor/image/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上傳儲存路徑,可以自定義儲存路徑和檔名格式 */
                                /* {filename} 會替換成原檔名,配置這項需要注意中文亂碼問題 */
                                /* {rand:6} 會替換成隨機數,後面的數字是隨機數的位數 */
                                /* {time} 會替換成時間戳 */
                                /* {yyyy} 會替換成四位年份 */
                                /* {yy} 會替換成兩位年份 */
                                /* {mm} 會替換成兩位月份 */
                                /* {dd} 會替換成兩位日期 */
                                /* {hh} 會替換成兩位小時 */
                                /* {ii} 會替換成兩位分鐘 */
                                /* {ss} 會替換成兩位秒 */
                                /* 非法字元 \ : * ? " < > | */
                                /* 具請體看線上文件: fex.baidu.com/ueditor/#use-format_upload_filename */

    /* 塗鴉圖片上傳配置項 */
    "scrawlActionName": "uploadscrawl", /* 執行上傳塗鴉的action名稱 */
    "scrawlFieldName": "upfile", /* 提交的圖片表單名稱 */
    "scrawlPathFormat": "/document/ueditor/image/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上傳儲存路徑,可以自定義儲存路徑和檔名格式 */
    "scrawlMaxSize": 2048000, /* 上傳大小限制,單位B */
    "scrawlUrlPrefix":"", /* 圖片訪問路徑字首 */
    "scrawlInsertAlign": "none",

    /* 截圖工具上傳 */
    "snapscreenActionName": "uploadimage", /* 執行上傳截圖的action名稱 */
    "snapscreenPathFormat": "/document/ueditor/image/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上傳儲存路徑,可以自定義儲存路徑和檔名格式 */
    "snapscreenUrlPrefix":"", /* 圖片訪問路徑字首 */
    "snapscreenInsertAlign": "none", /* 插入的圖片浮動方式 */

    /* 抓取遠端圖片配置 */
    "catcherLocalDomain": ["127.0.0.1", "localhost", "img.baidu.com"],
    "catcherActionName": "catchimage", /* 執行抓取遠端圖片的action名稱 */
    "catcherFieldName": "source", /* 提交的圖片列表表單名稱 */
    "catcherPathFormat": "/document/ueditor/image/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上傳儲存路徑,可以自定義儲存路徑和檔名格式 */
    "catcherUrlPrefix":"", /* 圖片訪問路徑字首 */
    "catcherMaxSize": 2048000, /* 上傳大小限制,單位B */
    "catcherAllowFiles": [".png", ".jpg", ".jpeg", ".gif", ".bmp"], /* 抓取圖片格式顯示 */

    /* 上傳視訊配置 */
    "videoActionName": "uploadvideo", /* 執行上傳視訊的action名稱 */
    "videoFieldName": "upfile", /* 提交的視訊表單名稱 */
    "videoPathFormat": "/document/ueditor/video/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上傳儲存路徑,可以自定義儲存路徑和檔名格式 */
    "videoUrlPrefix": "", /* 視訊訪問路徑字首 */
    "videoMaxSize": 102400000, /* 上傳大小限制,單位B,預設100MB */
    "videoAllowFiles": [
        ".flv", ".swf", ".mkv", ".avi", ".rm", ".rmvb", ".mpeg", ".mpg",
        ".ogg", ".ogv", ".mov", ".wmv", ".mp4", ".webm", ".mp3", ".wav", ".mid"], /* 上傳視訊格式顯示 */

    /* 上傳檔案配置 */
    "fileActionName": "uploadfile", /* controller裡,執行上傳視訊的action名稱 */
    "fileFieldName": "upfile", /* 提交的檔案表單名稱 */
    "filePathFormat": "/document/ueditor/file/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上傳儲存路徑,可以自定義儲存路徑和檔名格式 */
    "fileUrlPrefix":"", /* 檔案訪問路徑字首 */
    "fileMaxSize": 307200000, /* 上傳大小限制,單位B,預設50MB */
    "fileAllowFiles": [
        ".png", ".jpg", ".jpeg", ".gif", ".bmp",
        ".flv", ".swf", ".mkv", ".avi", ".rm", ".rmvb", ".mpeg", ".mpg",
        ".ogg", ".ogv", ".mov", ".wmv", ".mp4", ".webm", ".mp3", ".wav", ".mid",
        ".rar", ".zip", ".tar", ".gz", ".7z", ".bz2", ".cab", ".iso",
        ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".pdf", ".txt", ".md", ".xml"
    ], /* 上傳檔案格式顯示 */

    /* 列出指定目錄下的圖片 */
    "imageManagerActionName": "listimage", /* 執行圖片管理的action名稱 */
    "imageManagerListPath": "/document/ueditor/image/", /* 指定要列出圖片的目錄 */
    "imageManagerListSize": 20, /* 每次列出檔案數量 */
    "imageManagerUrlPrefix":"", /* 圖片訪問路徑字首 */
    "imageManagerInsertAlign": "none", /* 插入的圖片浮動方式 */
    "imageManagerAllowFiles": [".png", ".jpg", ".jpeg", ".gif", ".bmp"], /* 列出的檔案型別 */

    /* 列出指定目錄下的檔案 */
    "fileManagerActionName": "listfile", /* 執行檔案管理的action名稱 */
    "fileManagerListPath": "/document/ueditor/file/", /* 指定要列出檔案的目錄 */
    "fileManagerUrlPrefix":"", /* 檔案訪問路徑字首 */
    "fileManagerListSize": 20, /* 每次列出檔案數量 */
    "fileManagerAllowFiles": [
        ".png", ".jpg", ".jpeg", ".gif", ".bmp",
        ".flv", ".swf", ".mkv", ".avi", ".rm", ".rmvb", ".mpeg", ".mpg",
        ".ogg", ".ogv", ".mov", ".wmv", ".mp4", ".webm", ".mp3", ".wav", ".mid",
        ".rar", ".zip", ".tar", ".gz", ".7z", ".bz2", ".cab", ".iso",
        ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".pdf", ".txt", ".md", ".xml"
    ] /* 列出的檔案型別 */

}

注意點 1 :如果你是按照我的上一篇隨筆完成的對ueditor的初始配置,那麼在你引入ueditor控制元件的那個html檔案裡,一定存在這麼一段程式碼:

<script type="text/javascript">
    window.UEDITOR_HOME_URL = "/UEditor/";
</script>

這次在ueditor.config.js檔案中,我指定了這個路徑,所以在頁面裡的這個宣告就可以去掉了。而且我留著這裡是存在問題的,當然我不太確定是不是這裡導致的問題,如果大家配置完沒有問題,那這裡保留著也可以。

注意點 2 :這裡面有幾個路徑需要再提醒一下

在CommonController中: @RequestMapping("uploadimage")和 @RequestMapping("uploadfile") 需要和 config.json中的imageActionName一一對應。

config.json中:"basePath": "D:/scinece-2.0" 需要是你儲存在磁碟的路徑,當然這裡好像不改也行,因為這裡被新寫的controller覆蓋了,可以嘗試不改看看有沒有問題,博主保險起見就一起改了。

CommonController 中的 map.put("url", "/PathFile/"+nowName);需要和對映器類的registry.addResourceHandler("/PathImage/**").addResourceLocations("file:/D:/science-2.0/"); 一一對應

CommonController 中的 rootPath = afterPath+ "static/UEditor/jsp";是相對於專案的classpath的路徑

對於SpringBoot專案來說,classpath指的是src.main.java和src.main.resources路徑以及第三方jar包的根路徑,存放在這兩個路徑下的檔案,都可以通過classpath作為相對路徑來引用;

因為在maven專案打包之後,會將專案變成如下圖所示的路徑,也就是說 /java 和 /resources 這兩個資料夾所在的路徑會消失springboot專案會直接識別這兩個資料夾裡面的內容,****所以我們寫路徑的時候就直接可以從 resources 目錄的下一級開始,比如從/static或者 /template等等開始。

分享完畢,有不準確的地方大家交流指正,希望對大家有幫助!

相關文章