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

来源:https://www.cnblogs.com/rainbow-1/archive/2022/06/16/16383695.html
-Advertisement-
Play Games

springboot整合ueditor實現圖片上傳和文件上傳功能 寫在前面: 在閱讀本篇之前,請先按照我的這篇隨筆完成對ueditor的前期配置工作: springboot+layui 整合百度富文本編輯器ueditor入門使用教程(踩過的坑)https://www.cnblogs.com/rain ...


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等等開始。

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

好看請贊,養成習慣!

本文來自博客園,作者:靠譜楊,轉載請註明原文鏈接:https://www.cnblogs.com/rainbow-1/p/16383695.html


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Ubuntu中fcitx安裝失敗問題的原因是ubuntu用的是國外鏡像源,部分軟體下載不了。 對策: 更換國內鏡像源可以使 Ubuntu 的第三方軟體包的下載速度大步提升,這裡以更換為阿裡源為例: 源文件的相關信息在“/etc/apt/”路徑下,使用cd /etc/apt進入文件 源鏈接存放在文件[ ...
  • Memory Resource Controller 記憶體資源控制器 註意: 這個文檔完完全全地過時了,需要整個地重寫。但它還是包含了有用的信息,所以我們仍舊把它保留在這裡,但是如果你需要深入理解的話,需要確保核對過當前的代碼。 註意: 記憶體資源控制器在本文檔中指的是記憶體控制器。不要混淆了這裡記憶體控 ...
  • 延時統計Delay accounting 任務在執行時等待某個內核資源會意外遇到延遲,例如可運行的任務正在等待空閑CPU。 per-task的延時統計功能測量下列情況下任務經歷的延遲: 正在等待CPU,waiting for a CPU (while being runnable) 同步塊I/O的完 ...
  • 一、視圖概念 • 視圖概念 • 視圖是基於一個表或多個表或視圖的邏輯表(虛表),本身不包含數據,通過它可以對錶裡面的數據進行查詢。 • 基表 • 視圖是從一個或多個實際表中獲得的,這些表的數據存放在資料庫中。那些用於產生視圖的表叫做該視圖的基表。 • 視圖優點 • 簡化性 • 安全性 二、視圖分類 ...
  • 一、索引概念 • 在關係資料庫中,索引是一種與表有關的資料庫對象,它可以使對應於表的SQL查詢語句執行得更快。 • 索引的作用類似於圖書的目錄,可以根據目錄中的頁碼快速找到所需的內容。 • 對於資料庫來說,索引是一個必選項,對於現在的各種大型資料庫來說,索引可以大大提高資料庫的性能,以至於它變成了數 ...
  • 一、觸發器簡介 • 什麼是觸發器(trigger) • 觸發器在資料庫里以獨立的對象存儲,由資料庫產生某一類事件而啟動運行,即觸發器是當某個事件發生時自動地隱式運行。 • 觸發器的特點 • 由特定事件觸發,外部無法調用。 • 觸發器無參數。 • 一個表上最多可有12個觸發器。 • 觸發器的作用 • ...
  • 騰訊雲資料庫TDSQL與中國人民大學最新聯合研究成果被SIGMOD 2022接收並將通過長文形式發表。SIGMOD是國際數據管理與資料庫領域頂尖的學術會議之一,騰訊雲資料庫TDSQL論文已連續多年入選VLDB、SIGMOD、ICDE等國際頂級會議。 本次入選論文題目為:CompressDB: Ena ...
  • 本文介紹什麼是 SQL GROUPING 運算符,如何使用 SQL GROUPING 運算符。GROUPING 指示是否聚合 GROUP BY 列表中的指定列表達式。 本文重點 只使用 GROUP BY 子句和聚合函數是無法同時得出小計和合計的。如果想要同時得到,可以使用 GROUPING 運算符。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...