說在前面 工作中會遇到很多需要使用富文本編輯器的地方,比如我現在發佈這篇文章離不開這個神器,而且現在網上編輯器太多了。記得之前,由於工作需要自己封裝過一個編輯器的公共插件,是用ckeditor改版的,目的是要相容公司所有項目,使用方便。廢話不多說,今天寫這篇文章,一是總結自己學習複習,二是關於FTP ...
說在前面
工作中會遇到很多需要使用富文本編輯器的地方,比如我現在發佈這篇文章離不開這個神器,而且現在網上編輯器太多了。記得之前,由於工作需要自己封裝過一個編輯器的公共插件,是用ckeditor改版的,目的是要相容公司所有項目,使用方便。廢話不多說,今天寫這篇文章,一是總結自己學習複習,二是關於FTP上傳官方資料太少,也便於他人少趟坑,我在這裡會說的很細很明白,希望親們以後不要中槍了!
關於編輯器的簡單部署
去官網下載後,我們需要把下載的編輯器文件夾,摘一部分放到項目中,下載之後的目錄,如下圖(我這裡用的是jsp簡化版 1.2.2)
我在外面新建了個插件目錄umeditor,如下圖:
除了jsp文件夾,其餘拷到項目靜態資源的目錄中,然後 我們來分析jsp文件夾都有哪些東東?
這裡,我把那兩個jar包帶入到項目中,這裡我改了名字,方便導入,fileupload那個jar包項目之前就有。如下圖:
接下來,就是頁面調用了,很簡單,首先將編輯器所需要的樣式和腳本文件引入:
/**以上省略,這裡引入到頁面頭部**/ <link href="${ctx}/static/umeditor/themes/default/css/umeditor.css" type="text/css" rel="stylesheet"> <script type="text/javascript" charset="utf-8" src="${ctx}/static/umeditor/umeditor.config.js"></script> <script type="text/javascript" charset="utf-8" src="${ctx}/static/umeditor/umeditor.min.js"></script> <script type="text/javascript" src="${ctx}/static/umeditor/lang/zh-cn/zh-cn.js"></script> </head>
接下來,需要在頁面調用編輯器對象,官方用的是script標簽,我這裡用textarea來構造
<div class="controls"> <form:textarea id="content" htmlEscape="true" path="articleData.content" rows="4" maxlength="200" class="input-xxlarge"/> <script type="text/javascript"> //實例化編輯器 var um = UM.getEditor('content'); //註意ID的一致 um.setWidth(700); //設置編輯器寬度 </script> </div>
至此基本的工作已做完,上述相信大部分搞開發的同胞們,都沒問題吧!效果圖如下:
關於編輯器的後臺實現
官方下載給的後臺處理文件除了必要的jar外有三個文件:
(1) Uploader.java 文件
該文件主封裝了一個文件上傳對象,包括上傳後需要返回的參數、狀態、URL,除此還有一個上傳的處理函數,當然只是普通的文件存儲,不符合我們的需求,但是我們可以知道前端需要的一些返回值。
(2) imageUp.jsp 文件
上傳圖片預設配置後臺處理文件,主要調用Upload上傳類,完成上傳 並把上傳結果 以json字元串發往前臺,其中包括重要的state、url等參數
(3) getContent.jsp 文件 (和上傳沒關係,略過)
以上分析過官方給的簡單後臺處理邏輯,我們不難知道實際上就是需要我們提供一個上傳處理函數並返回包含必要參數的JSON串
那好,接下來我們開始寫自己的後臺上傳處理函數了。
第一步 後臺控制器處理函數(重要程度:☆☆☆☆☆)
1 /** 2 * 編輯器上傳圖片 3 */ 4 @RequiresPermissions("cms:article:view") 5 @RequestMapping(value={"/upload"}) 6 @ResponseBody() 7 public String upload(HttpServletRequest request,HttpServletResponse response){ 8 9 //request.getParameter("path"); 10 String[] allowExtName = {".jpg",".png",".gif",".bmp",".jpeg"};//圖片格式限制 11 List<MultipartFile> multipartFiles = getFileSet(request, 1024 * 1024 * 10, allowExtName); //上傳的圖片大小可以放到配置中讀取,這裡設置10M 12 Map<String,Object> map = new HashMap<String,Object>(); 13 try { 14 if(multipartFiles.size() > 0){ 15 MultipartFile file = multipartFiles.get(0); 16 InputStream is = file.getInputStream(); 17 String tempFileName = file.getOriginalFilename(); 18 if(is != null&&StringUtils.isNotBlank(tempFileName)){ 19 20 //生成文件名 21 String uuid = IdGen.uuid(); //生成的一個隨機字元串,用於圖片名 22 String fileName = uuid+tempFileName.substring(tempFileName.indexOf(".")); 23 //生成文件路徑 24 boolean ftpWaterRs=true; 25 FTPUtils ftpUtils = FTPUtils.getInstance(); 26 SimpleDateFormat sf = new SimpleDateFormat("yyyy/MM/"); 27 String ss = sf.format(new Date()); //以當前時間,生成存放目錄,格式/yyyy/MM 28 String storePath = ftpUtils.getSaveFilePath() + ss; //讀取配置的存儲目錄 比如 /upload/image/ 29 //圖片加水印 getResourceRootRealPath ; 若圖片大小小於logo大小則不加水印 30 if(file.getSize()>1200){ //這裡給圖片增加水印功能 31 String waterName= uuid + "_water"+tempFileName.substring(tempFileName.indexOf(".")); 32 //緩存文件類型轉換 33 CommonsMultipartFile cf= (CommonsMultipartFile)file; 34 DiskFileItem fi = (DiskFileItem)cf.getFileItem(); 35 File tempFile = fi.getStoreLocation(); 36 String waterTempPath = SpringContextHolder.getRootRealPath()+"/"+waterName; 37 String logoPath=SpringContextHolder.getRootRealPath()+"/static/images/shuiyin.png"; //水印圖片路徑 38 ImageUtils.markImageByIcon(logoPath, tempFile, waterTempPath, 45); //添加水印 39 File waterFile = new File(waterTempPath); 40 //上傳水印圖片 41 ftpWaterRs = ftpUtils.storeFile(storePath,waterName,new FileInputStream(waterFile)); 42 if(ftpWaterRs){ 43 FileUtils.deleteFile(waterTempPath); 44 is.close(); 45 map.clear(); 46 map.put("state","SUCCESS"); //註意:返回的參數state 成功必須是 SUCCESS,否則需要到image.js中改,失敗可以自定義 47 //map.put("url",ftpUtils.getSiteName().trim()+storePath.trim() + waterName); 48 map.put("url",storePath.trim() + waterName);
//url 這裡有個坑,絕對完整地址圖片不會顯示
//我現在返回的是不包含功能變數名稱的路徑 如 /upload/images/2016/08/03/a23ssds6s6d56ds656a6a5652636.jpg
//功能變數名稱部分路徑也就是http://static.xx.com/ 需要到前端配置,具體是 在umeditor.config.js 配置參數 imagePath 所謂的圖片修正地址嘍 49 return JsonMapper.toJsonString(map); 50 } 51 } 52 53 //上傳源文件 54 boolean ftpFileRs = ftpUtils.storeFile(storePath, fileName, is); 55 is.close(); 56 if(ftpFileRs){ //這裡水印圖片上傳失敗 會採用原圖 57 map.clear(); 58 map.put("state","SUCCESS"); 59 map.put("url",storePath.trim() + fileName); 60 return JsonMapper.toJsonString(map); 61 } 62 } 63 } 64 else{ 65 map.clear(); 66 map.put("state","請檢查圖片格式或尺寸,圖片必須小於10M"); 67 return JsonMapper.toJsonString(map); 68 } 69 } catch (Exception e) { 70 e.printStackTrace(); 71 } 72 map.clear(); 73 map.put("state","上傳請求異常"); 74 return JsonMapper.toJsonString(map); 75 }
第二步 處理函數用到上傳圖片驗證函數包含大小和格式(重要程度:☆☆☆)
1 /** 2 * @descrption 根據HttpServletRequest對象獲取MultipartFile集合 3 * @author zp 4 * @param request 5 * @param maxLength 6 * 文件最大限制 7 * @param allowExtName 8 * 不允許上傳的文件擴展名 9 * @return MultipartFile集合 10 */ 11 public static List<MultipartFile> getFileSet(HttpServletRequest request, 12 long maxLength, String[] allowExtName) { 13 MultipartHttpServletRequest multipartRequest = null; 14 try { 15 multipartRequest = (MultipartHttpServletRequest) request; 16 } catch (Exception e) { 17 return new LinkedList<MultipartFile>(); 18 } 19 20 List<MultipartFile> files = new LinkedList<MultipartFile>(); 21 files = multipartRequest.getFiles("upfile"); //upfile 是編輯器預設的上傳圖片表單name,在文件umeditor.config.js 可自定義配置參數 imageFieldName 22 // 移除不符合條件的 23 for (int i = 0; i < files.size(); i++) { 24 if (!validateFile(files.get(i), maxLength, allowExtName)) { 25 files.remove(files.get(i)); 26 if (files.size() == 0) { 27 return files; 28 } 29 } 30 } 31 return files; 32 } 33 34 /** 35 * @descrption 驗證文件格式,這裡主要驗證尾碼名 36 * @author zp 37 * @param file 38 * MultipartFile對象 39 * @param maxLength 40 * 文件最大限制 41 * @param allowExtName 42 * 不允許上傳的文件擴展名 43 * @return 文件格式是否合法 44 */ 45 private static boolean validateFile(MultipartFile file, long maxLength, 46 String[] allowExtName) { 47 if (file.getSize() < 0 || file.getSize() > maxLength) 48 return false; 49 String filename = file.getOriginalFilename(); 50 51 // 處理不選擇文件點擊上傳時,也會有MultipartFile對象,在此進行過濾 52 if (filename == "") { 53 return false; 54 } 55 String extName = filename.substring(filename.lastIndexOf(".")) 56 .toLowerCase(); 57 if (allowExtName == null || allowExtName.length == 0 58 || Arrays.binarySearch(allowExtName, extName) != -1) { 59 return true; 60 } else { 61 return false; 62 } 63 }
第三步 FTP上傳處理類,絕對福利..好多人想要哦 0.0(重要程度:☆☆☆☆)
1 package com.xx.utils; 2 3 import java.io.File; 4 import java.io.FileInputStream; 5 import java.io.IOException; 6 import java.io.InputStream; 7 8 import org.apache.commons.net.ftp.FTPClient; 9 import org.apache.commons.net.ftp.FTPFile; 10 import org.apache.commons.net.ftp.FTPReply; 11 12 import com.xx.Global; 13 14 /** 15 * FTP伺服器工具類 16 */ 17 public class FTPUtils { 18 19 private static FTPUtils ftpUtils; 20 private FTPClient ftpClient; 21 //private FTPFile ftpFile; 22 private String port; // 伺服器埠 23 private String username; // 用戶登錄名 24 private String password; // 用戶登錄密碼 25 private String serverName; // 服務名 26 private int localPasv;//開啟本地被動模式 27 private String siteName; // 站點功能變數名稱 28 private String saveFilePath;//存儲路徑 29 30 31 private InputStream is; // 文件下載輸入流 32 33 /** 34 * 私有構造方法 35 */ 36 private FTPUtils() { 37 initConfig(); 38 if (null == ftpClient) { 39 ftpClient = new FTPClient(); 40 } 41 } 42 43 /** 44 * 獲取FTPUtils對象實例 45 * @return 46 * FTPUtils對象實例 47 */ 48 public synchronized static FTPUtils getInstance () { 49 if (null == ftpUtils) { 50 ftpUtils = new FTPUtils(); 51 } 52 return ftpUtils; 53 } 54 55 /** 56 * 初始化FTP伺服器連接屬性 57 */ 58 // public void initConfig () { 59 // // 構造Properties對象 60 // Properties properties = new Properties(); 61 // 62 // // 定義配置文件輸入流 63 // InputStream is = null; 64 // try { 65 // // 獲取配置文件輸入流 66 // is = FTPUtils.class.getResourceAsStream("/ftp.properties"); 67 // // 載入配置文件 68 // properties.load(is); 69 // // 讀取配置文件 70 // port = (String) properties.get("port"); // 設置埠 71 // username = (String) properties.get("username1"); // 設置用戶名 72 // password = (String) properties.get("password1"); // 設置密碼 73 // serverName = (String) properties.get("serverName"); // 服務名 74 // localPasv = Integer.valueOf(String.valueOf(properties.get("localPasv"))); 75 // } catch (IOException e) { 76 // e.printStackTrace(); 77 // } finally { 78 // // 判斷輸入流是否為空 79 // if (null != is) { 80 // try { 81 // // 關閉輸入流 82 // is.close(); 83 // } catch (IOException e) { 84 // e.printStackTrace(); 85 // } 86 // } 87 // } 88 // } 89 90 public void initConfig () { 91 serverName = Global.getConfig("ftp.serverName"); 92 // SystemConfig.getInstance().getApplication().get("ftp.serverName"); 93 port = Global.getConfig("ftp.port"); 94 // SystemConfig.getInstance().getApplication().get("ftp.port"); 95 username = Global.getConfig("ftp.username1"); 96 // SystemConfig.getInstance().getApplication().get("ftp.username1"); 97 password =Global.getConfig("ftp.password1"); 98 // portSystemConfig.getInstance().getApplication().get("ftp.password1"); 99 localPasv = Integer.valueOf(Global.getConfig("ftp.localPasv")); 100 // Integer.valueOf(SystemConfig.getInstance().getApplication().get("ftp.localPasv")); 101 siteName = Global.getConfig("ftp.readPath"); //讀取配置 訪問路徑 102 saveFilePath = Global.getConfig("ftp.upLoadPath"); //讀取配置 上傳路徑 103 104 } 105 /** 106 * 連接(配置通用連接屬性)至伺服器 107 * 108 * @param serverName 109 * 伺服器名稱 110 * @param remotePath 111 * 當前訪問目錄 112 * @return 113 * <b>true</b>:連接成功 114 * <br/> 115 * <b>false</b>:連接失敗 116 */ 117 public boolean connectToTheServer (String remotePath) { 118 // 定義返回值 119 boolean result = false; 120 try { 121 // 連接至伺服器,埠預設為21時,可直接通過URL連接 122 ftpClient.connect(serverName, Integer.parseInt(port)); 123 // 登錄伺服器 124 ftpClient.login(username, password); 125 // 判斷返回碼是否合法 126 if (!FTPReply.isPositiveCompletion(ftpClient.getReplyCode())) { 127 // 不合法時斷開連接 128 ftpClient.disconnect(); 129 // 結束程式 130 return result; 131 } 132 if(localPasv==1) 133 ftpClient.enterLocalPassiveMode(); 134 // 設置文件操作目錄 135 result = createDirAndToDir(remotePath); 136 System.out.println("result===="+result); 137 // 設置文件類型,二進位 138 result = ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE); 139 // 設置緩衝區大小 140 ftpClient.setBufferSize(3072); 141 // 設置字元編碼 142 ftpClient.setControlEncoding("UTF-8"); 143 } catch (IOException e) { 144 e.printStackTrace(); 145 } 146 return result; 147 } 148 149 /** 150 * 上傳文件至FTP伺服器 151 * 152 * @param serverName 153 * 伺服器名稱 154 * @param storePath 155 * 上傳文件存儲路徑 156 * @param fileName 157 * 上傳文件存儲名稱 158 * @param is 159 * 上傳文件輸入流 160 * @return 161 * <b>true</b>:上傳成功 162 * <br/> 163 * <b>false</b>:上傳失敗 164 */ 165 public boolean storeFile (String storePath, String fileName, InputStream is) { 166 boolean result = false; 167 try { 168 // 連接至伺服器 169 result = connectToTheServer(storePath); 170 // 判斷伺服器是否連接成功 171 if (result) { 172 // 上傳文件 173 result = ftpClient.storeFile(fileName, is); 174 } 175 // 關閉輸入流 176 is.close(); 177 } catch (IOException e) { 178 e.printStackTrace(); 179 } finally { 180 // 判斷輸入流是否存在 181 if (null != is) { 182 try { 183 // 關閉輸入流 184 is.close(); 185 } catch (IOException e) { 186 e.printStackTrace(); 187 } 188 } 189 // 登出伺服器並斷開連接 190 ftpUtils.logout(); 191 } 192 return result; 193 } 194 195 /** 196 * 下載FTP伺服器文件至本地<br/> 197 * 操作完成後需調用logout方法與伺服器斷開連接 198 * 伺服器名稱 199 * @param remotePath 200 * 下載文件存儲路徑 201 * @param fileName 202 * 下載文件存儲名稱 203 * @return 204 * <b>InputStream</b>:文件輸入流 205 */ 206 public InputStream retrieveFile (String remotePath, String fileName) { 207 try { 208 boolean result = false; 209 // 連接至伺服器 210 result = connectToTheServer(remotePath); 211 // 判斷伺服器是否連接成功 212 if (result) { 213 // 獲取文件輸入流 214 is = ftpClient.retrieveFileStream(fileName); 215 } 216 } catch (IOException e) { 217 e.printStackTrace(); 218 } 219 return is; 220 } 221 222 /** 223 * 刪除FTP伺服器文件 224 * 225 * @param serverName 226 * 伺服器名稱 227 * @param remotePath 228 * 當前訪問目錄 229 * @param fileName 230 * 文件存儲名稱 231 * @return 232 * <b>true</b>:刪除成功 233 * <br/> 234 * <b>false</b>:刪除失敗 235 */ 236 public boolean deleteFile (String serverName, String remotePath, String fileName) { 237 boolean result = false; 238 // 連接至伺服器 239 result = connectToTheServer(remotePath); 240 // 判斷伺服器是否連接成功 241 if (result) { 242 try