说在前面

工作中会遇到很多需要使用富文本编辑器的地方,比如我现在发布这篇文章离不开这个神器,而且现在网上编辑器太多了。记得之前,由于工作需要自己封装过一个编辑器的公共插件,是用ckeditor改版的,目的是要兼容公司所有项目,使用方便。废话不多说,今天写这篇文章,一是总结自己学习复习,二是关于FTP上传官方资料太少,也便于他人少趟坑,我在这里会说的很细很明白,希望亲们以后不要中枪了!

关于编辑器的简单部署

去官网下载后,我们需要把下载的编辑器文件夹,摘一部分放到项目中,下载之后的目录,如下图(我这里用的是jsp简化版 1.2.2)

我在外面新建了个插件目录umeditor,如下图:

除了jsp文件夹,其余拷到项目静态资源的目录中,然后 我们来分析jsp文件夹都有哪些东东?

这里,我把那两个jar包带入到项目中,这里我改了名字,方便导入,fileupload那个jar包项目之前就有。如下图:

接下来,就是页面调用了,很简单,首先将编辑器所需要的样式和脚本文件引入:

  1. /**以上省略,这里引入到页面头部**/
  2. <link href="${ctx}/static/umeditor/themes/default/css/umeditor.css" type="text/css" rel="stylesheet">
  3. <script type="text/javascript" charset="utf-8" src="${ctx}/static/umeditor/umeditor.config.js"></script>
  4. <script type="text/javascript" charset="utf-8" src="${ctx}/static/umeditor/umeditor.min.js"></script>
  5. <script type="text/javascript" src="${ctx}/static/umeditor/lang/zh-cn/zh-cn.js"></script>
  6. </head>

接下来,需要在页面调用编辑器对象,官方用的是script标签,我这里用textarea来构造

  1. <div class="controls">
  2. <form:textarea id="content" htmlEscape="true" path="articleData.content" rows="4" maxlength="200" class="input-xxlarge"/>
  3.  
  4. <script type="text/javascript">
  5. //实例化编辑器
  6. var um = UM.getEditor('content'); //注意ID的一致
  7. um.setWidth(700); //设置编辑器宽度
  8. </script>
  9. </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. private InputStream is; // 文件下载输入流
  31.  
  32. /**
  33. * 私有构造方法
  34. */
  35. private FTPUtils() {
  36. initConfig();
  37. if (null == ftpClient) {
  38. ftpClient = new FTPClient();
  39. }
  40. }
  41.  
  42. /**
  43. * 获取FTPUtils对象实例
  44. * @return
  45. * FTPUtils对象实例
  46. */
  47. public synchronized static FTPUtils getInstance () {
  48. if (null == ftpUtils) {
  49. ftpUtils = new FTPUtils();
  50. }
  51. return ftpUtils;
  52. }
  53.  
  54. /**
  55. * 初始化FTP服务器连接属性
  56. */
  57. // public void initConfig () {
  58. // // 构造Properties对象
  59. // Properties properties = new Properties();
  60. //
  61. // // 定义配置文件输入流
  62. // InputStream is = null;
  63. // try {
  64. // // 获取配置文件输入流
  65. // is = FTPUtils.class.getResourceAsStream("/ftp.properties");
  66. // // 加载配置文件
  67. // properties.load(is);
  68. // // 读取配置文件
  69. // port = (String) properties.get("port"); // 设置端口
  70. // username = (String) properties.get("username1"); // 设置用户名
  71. // password = (String) properties.get("password1"); // 设置密码
  72. // serverName = (String) properties.get("serverName"); // 服务名
  73. // localPasv = Integer.valueOf(String.valueOf(properties.get("localPasv")));
  74. // } catch (IOException e) {
  75. // e.printStackTrace();
  76. // } finally {
  77. // // 判断输入流是否为空
  78. // if (null != is) {
  79. // try {
  80. // // 关闭输入流
  81. // is.close();
  82. // } catch (IOException e) {
  83. // e.printStackTrace();
  84. // }
  85. // }
  86. // }
  87. // }
  88.  
  89. public void initConfig () {
  90. serverName = Global.getConfig("ftp.serverName");
  91. // SystemConfig.getInstance().getApplication().get("ftp.serverName");
  92. port = Global.getConfig("ftp.port");
  93. // SystemConfig.getInstance().getApplication().get("ftp.port");
  94. username = Global.getConfig("ftp.username1");
  95. // SystemConfig.getInstance().getApplication().get("ftp.username1");
  96. password =Global.getConfig("ftp.password1");
  97. // portSystemConfig.getInstance().getApplication().get("ftp.password1");
  98. localPasv = Integer.valueOf(Global.getConfig("ftp.localPasv"));
  99. // Integer.valueOf(SystemConfig.getInstance().getApplication().get("ftp.localPasv"));
  100. siteName = Global.getConfig("ftp.readPath"); //读取配置 访问路径
  101. saveFilePath = Global.getConfig("ftp.upLoadPath"); //读取配置 上传路径
  102.  
  103. }
  104. /**
  105. * 连接(配置通用连接属性)至服务器
  106. *
  107. * @param serverName
  108. * 服务器名称
  109. * @param remotePath
  110. * 当前访问目录
  111. * @return
  112. * <b>true</b>:连接成功
  113. * <br/>
  114. * <b>false</b>:连接失败
  115. */
  116. public boolean connectToTheServer (String remotePath) {
  117. // 定义返回值
  118. boolean result = false;
  119. try {
  120. // 连接至服务器,端口默认为21时,可直接通过URL连接
  121. ftpClient.connect(serverName, Integer.parseInt(port));
  122. // 登录服务器
  123. ftpClient.login(username, password);
  124. // 判断返回码是否合法
  125. if (!FTPReply.isPositiveCompletion(ftpClient.getReplyCode())) {
  126. // 不合法时断开连接
  127. ftpClient.disconnect();
  128. // 结束程序
  129. return result;
  130. }
  131. if(localPasv==1)
  132. ftpClient.enterLocalPassiveMode();
  133. // 设置文件操作目录
  134. result = createDirAndToDir(remotePath);
  135. System.out.println("result===="+result);
  136. // 设置文件类型,二进制
  137. result = ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
  138. // 设置缓冲区大小
  139. ftpClient.setBufferSize(3072);
  140. // 设置字符编码
  141. ftpClient.setControlEncoding("UTF-8");
  142. } catch (IOException e) {
  143. e.printStackTrace();
  144. }
  145. return result;
  146. }
  147.  
  148. /**
  149. * 上传文件至FTP服务器
  150. *
  151. * @param serverName
  152. * 服务器名称
  153. * @param storePath
  154. * 上传文件存储路径
  155. * @param fileName
  156. * 上传文件存储名称
  157. * @param is
  158. * 上传文件输入流
  159. * @return
  160. * <b>true</b>:上传成功
  161. * <br/>
  162. * <b>false</b>:上传失败
  163. */
  164. public boolean storeFile (String storePath, String fileName, InputStream is) {
  165. boolean result = false;
  166. try {
  167. // 连接至服务器
  168. result = connectToTheServer(storePath);
  169. // 判断服务器是否连接成功
  170. if (result) {
  171. // 上传文件
  172. result = ftpClient.storeFile(fileName, is);
  173. }
  174. // 关闭输入流
  175. is.close();
  176. } catch (IOException e) {
  177. e.printStackTrace();
  178. } finally {
  179. // 判断输入流是否存在
  180. if (null != is) {
  181. try {
  182. // 关闭输入流
  183. is.close();
  184. } catch (IOException e) {
  185. e.printStackTrace();
  186. }
  187. }
  188. // 登出服务器并断开连接
  189. ftpUtils.logout();
  190. }
  191. return result;
  192. }
  193.  
  194. /**
  195. * 下载FTP服务器文件至本地<br/>
  196. * 操作完成后需调用logout方法与服务器断开连接
  197. * 服务器名称
  198. * @param remotePath
  199. * 下载文件存储路径
  200. * @param fileName
  201. * 下载文件存储名称
  202. * @return
  203. * <b>InputStream</b>:文件输入流
  204. */
  205. public InputStream retrieveFile (String remotePath, String fileName) {
  206. try {
  207. boolean result = false;
  208. // 连接至服务器
  209. result = connectToTheServer(remotePath);
  210. // 判断服务器是否连接成功
  211. if (result) {
  212. // 获取文件输入流
  213. is = ftpClient.retrieveFileStream(fileName);
  214. }
  215. } catch (IOException e) {
  216. e.printStackTrace();
  217. }
  218. return is;
  219. }
  220.  
  221. /**
  222. * 删除FTP服务器文件
  223. *
  224. * @param serverName
  225. * 服务器名称
  226. * @param remotePath
  227. * 当前访问目录
  228. * @param fileName
  229. * 文件存储名称
  230. * @return
  231. * <b>true</b>:删除成功
  232. * <br/>
  233. * <b>false</b>:删除失败
  234. */
  235. public boolean deleteFile (String serverName, String remotePath, String fileName) {
  236. boolean result = false;
  237. // 连接至服务器
  238. result = connectToTheServer(remotePath);
  239. // 判断服务器是否连接成功
  240. if (result) {
  241. try {
  242. // 删除文件
  243. result = ftpClient.deleteFile(fileName);
  244. } catch (IOException e) {
  245. e.printStackTrace();
  246. } finally {
  247. // 登出服务器并断开连接
  248. ftpUtils.logout();
  249. }
  250. }
  251. return result;
  252. }
  253.  
  254. /**
  255. * 创建目录
  256. *
  257. * @param remotePath
  258. * 目录储路径
  259. * @return
  260. * <b>true</b>:创建成功
  261. * <br/>
  262. * <b>false</b>:创建失败
  263. */
  264. public boolean createDirAndToDir (String remotePath) {
  265. boolean result = false;
  266. try {
  267. // 连接至服务器
  268. //result = ftpClient.changeWorkingDirectory(remotePath);
  269. String [] dirs = remotePath.split("/");
  270. if(dirs!=null){
  271. String tempDir = "";
  272. for(String dir : dirs){
  273. tempDir += dir +"/";
  274. result = ftpClient.changeWorkingDirectory(tempDir);
  275. if(!result){
  276. result = ftpClient.makeDirectory(dir);
  277. ftpClient.changeWorkingDirectory(dir);
  278. }
  279. }
  280. }
  281. } catch (IOException e) {
  282. e.printStackTrace();
  283. return false;
  284. }
  285. return true;
  286. }
  287. /**
  288. * 检测FTP服务器文件是否存在
  289. * 服务器名称
  290. * @param remotePath
  291. * 检测文件存储路径
  292. * @param fileName
  293. * 检测文件存储名称
  294. * @return
  295. * <b>true</b>:文件存在
  296. * <br/>
  297. * <b>false</b>:文件不存在
  298. */
  299. public boolean checkFile (String remotePath, String fileName) {
  300. boolean result = false;
  301. try {
  302. // 连接至服务器
  303. result = connectToTheServer(remotePath);
  304. // 判断服务器是否连接成功
  305. if (result) {
  306. // 默认文件不存在
  307. result = false;
  308. // 获取文件操作目录下所有文件名称
  309. String[] remoteNames = ftpClient.listNames();
  310. // 循环比对文件名称,判断是否含有当前要下载的文件名
  311. for (String remoteName: remoteNames) {
  312. if (fileName.equals(remoteName)) {
  313. result = true;
  314. }
  315. }
  316. }
  317. } catch (IOException e) {
  318. e.printStackTrace();
  319. } finally {
  320. // 登出服务器并断开连接
  321. ftpUtils.logout();
  322. }
  323. return result;
  324. }
  325.  
  326. /**
  327. * 登出服务器并断开连接
  328. *
  329. * @param ftp
  330. * FTPClient对象实例
  331. * @return
  332. * <b>true</b>:操作成功
  333. * <br/>
  334. * <b>false</b>:操作失败
  335. */
  336. public boolean logout () {
  337. boolean result = false;
  338. if (null != is) {
  339. try {
  340. // 关闭输入流
  341. is.close();
  342. } catch (IOException e) {
  343. e.printStackTrace();
  344. }
  345. }
  346. if (null != ftpClient) {
  347. try {
  348. // 登出服务器
  349. result = ftpClient.logout();
  350. } catch (IOException e) {
  351. e.printStackTrace();
  352. } finally {
  353. // 判断连接是否存在
  354. if (ftpClient.isConnected()) {
  355. try {
  356. // 断开连接
  357. ftpClient.disconnect();
  358. } catch (IOException e) {
  359. e.printStackTrace();
  360. }
  361. }
  362. }
  363. }
  364. return result;
  365. }
  366.  
  367. public String getSiteName() {
  368. return siteName;
  369. }
  370.  
  371. public String getSaveFilePath() {
  372. return saveFilePath;
  373. } }

           第四步 用到的副类,真正做到任君品尝 !!(重要程度:☆☆

(1)IDGen工具类

  1. /**
  2. * 随机ID工具类
  3. */
  4. package com.xx.utils;
  5.  
  6. import java.io.Serializable;
  7. import java.security.SecureRandom;
  8. import java.text.DecimalFormat;
  9. import java.util.Date;
  10. import java.util.UUID;
  11.  
  12. import org.apache.shiro.session.Session;
  13. import org.apache.shiro.session.mgt.eis.SessionIdGenerator;
  14. import org.springframework.context.annotation.Lazy;
  15. import org.springframework.stereotype.Service;
  16.  
  17. /**
  18. * 封装各种生成唯一性ID算法的工具类.
  19. * @author ThinkGem
  20. * @version 2013-01-15
  21. */
  22. @Service
  23. @Lazy(false)
  24. public class IdGen implements SessionIdGenerator {
  25.  
  26. private static SecureRandom random = new SecureRandom();
  27. private static int num;
  28.  
  29. /**
  30. * 封装JDK自带的UUID, 通过Random数字生成, 中间无-分割.
  31. */
  32. public static String uuid() {
  33. return UUID.randomUUID().toString().replaceAll("-", "");
  34. }
  35. /**
  36. * 生成投资编号
  37. * @return
  38. */
  39. public static String investNo(){
  40. String date = DateUtils.formatDateToStr("yyyyMMddHHmmss",new Date());
  41. if(num>999999){
  42. num = 0;
  43. }
  44. String numStr = new DecimalFormat("000000").format(num++);
  45. return date.substring(2)+ numStr;
  46. }
  47.  
  48. /**
  49. * 使用SecureRandom随机生成Long.
  50. */
  51. public static long randomLong() {
  52. return Math.abs(random.nextLong());
  53. }
  54.  
  55. @Override
  56. public Serializable generateId(Session session) {
  57. return IdGen.uuid();
  58. }
  59.  
  60. public static String getRandomName(int k){
  61. String chars = "abcdefghijklmnopqrstuvwxyz0123456789";
  62. StringBuilder sb = new StringBuilder();
  63. for (int i = 0; i < k; i++) {
  64. sb.append(chars.charAt((int)(Math.random() * 36)));
  65. }
  66. return sb.toString();
  67. }
  68.  
  69. }

(2)ImageUtil工具类

  1. package com.xx.utils;
  2.  
  3. import java.awt.AlphaComposite;
  4. import java.awt.Color;
  5. import java.awt.Font;
  6. import java.awt.Graphics2D;
  7. import java.awt.Image;
  8. import java.awt.RenderingHints;
  9. import java.awt.image.BufferedImage;
  10. import java.io.File;
  11. import java.io.FileOutputStream;
  12. import java.io.InputStream;
  13. import java.io.OutputStream;
  14.  
  15. import javax.imageio.ImageIO;
  16. import javax.swing.ImageIcon;
  17.  
  18. import org.slf4j.Logger;
  19. import org.slf4j.LoggerFactory;
  20.  
  21. /**
  22. * 图片处理类
  23. * @author xx
  24. */
  25. public class ImageUtils {
  26. private final static Logger logger = LoggerFactory.getLogger(ImageUtils.class);
  27. // 水印透明度
  28. private static float alpha = 0.5f;
  29. // 水印横向位置
  30. private static int positionWidth = 150;
  31. // 水印纵向位置
  32. private static int positionHeight = 300;
  33. // 水印文字字体
  34. private static Font font = new Font("宋体", Font.BOLD, 30);
  35. // 水印文字颜色
  36. private static Color color = Color.red;
  37.  
  38. /**
  39. * 给图片添加图片水印
  40. * @param iconPath 水印图片路径
  41. * @param file 源文件
  42. * @param targerPath 目标图片路径
  43. * @param degree 水印图片旋转角度
  44. */
  45. public static void markImageByIcon(String iconPath, File file,
  46. String targerPath, Integer degree) {
  47. OutputStream os = null;
  48. try {
  49. logger.info("水印图片路径:{}", iconPath);
  50. logger.info("源文件:{}", file.getAbsolutePath());
  51. logger.info("目标图片路径:{}", targerPath);
  52. Image srcImg = ImageIO.read(file);
  53.  
  54. BufferedImage buffImg = new BufferedImage(srcImg.getWidth(null),
  55. srcImg.getHeight(null), BufferedImage.TYPE_INT_RGB);
  56.  
  57. // 得到画笔对象
  58. Graphics2D g = buffImg.createGraphics();
  59.  
  60. // 设置对线段的锯齿状边缘处理
  61. g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
  62. RenderingHints.VALUE_INTERPOLATION_BILINEAR);
  63.  
  64. g.drawImage(srcImg.getScaledInstance(srcImg.getWidth(null), srcImg
  65. .getHeight(null), Image.SCALE_SMOOTH), 0, 0, null);
  66.  
  67. if (null != degree) {
  68. // 设置水印旋转
  69. g.rotate(Math.toRadians(degree),
  70. (double) buffImg.getWidth() / 2, (double) buffImg
  71. .getHeight() / 2);
  72. }
  73.  
  74. // 水印图象的路径 水印一般为gif或者png的,这样可设置透明度
  75. ImageIcon imgIcon = new ImageIcon(iconPath);
  76.  
  77. // 得到Image对象。
  78. Image img = imgIcon.getImage();
  79.  
  80. float alpha = 0.5f; // 透明度
  81. g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP,
  82. alpha));
  83.  
  84. // 表示水印图片的位置 相对于中心店的宽高以及水印图片宽高(img,x,y,width,height,obnull)
  85. g.drawImage(img, buffImg.getWidth() / 6,buffImg.getHeight() / 3, buffImg.getWidth() / 2,buffImg.getHeight() / 4, null);
  86.  
  87. g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
  88.  
  89. g.dispose();
  90.  
  91. os = new FileOutputStream(targerPath);
  92.  
  93. // 生成图片
  94. ImageIO.write(buffImg, "JPG", os);
  95.  
  96. logger.info("图片完成添加水印");
  97. } catch (Exception e) {
  98. e.printStackTrace();
  99. logger.error("图片完成添加水印error:{}", e.getMessage());
  100. } finally {
  101. try {
  102. if (null != os)
  103. os.close();
  104. } catch (Exception e) {
  105. e.printStackTrace();
  106. }
  107. }
  108. }
  109.  
  110. /**
  111. * 给图片添加水印文字
  112. * @param logoText 水印文字
  113. * @param srcImgPath 原图片路径
  114. * @param targerPath 目标图片路径
  115. * @param degree 旋转角度
  116. */
  117. public static void markImageByText(String logoText, String srcImgPath,
  118. String targerPath, Integer degree) {
  119.  
  120. InputStream is = null;
  121. OutputStream os = null;
  122. try {
  123. // 1、源图片
  124. Image srcImg = ImageIO.read(new File(srcImgPath));
  125. BufferedImage buffImg = new BufferedImage(srcImg.getWidth(null),srcImg.getHeight(null), BufferedImage.TYPE_INT_RGB);
  126.  
  127. // 2、得到画笔对象
  128. Graphics2D g = buffImg.createGraphics();
  129. // 3、设置对线段的锯齿状边缘处理
  130. g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,RenderingHints.VALUE_INTERPOLATION_BILINEAR);
  131. g.drawImage(srcImg.getScaledInstance(srcImg.getWidth(null), srcImg.getHeight(null), Image.SCALE_SMOOTH), 0, 0, null);
  132. // 4、设置水印旋转
  133. if (null != degree) {
  134. g.rotate(Math.toRadians(degree),(double) buffImg.getWidth() / 2, (double) buffImg.getHeight() / 2);
  135. }
  136. // 5、设置水印文字颜色
  137. g.setColor(color);
  138. // 6、设置水印文字Font
  139. g.setFont(font);
  140. // 7、设置水印文字透明度
  141. g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP,alpha));
  142. // 8、第一参数->设置的内容,后面两个参数->文字在图片上的坐标位置(x,y)
  143. g.drawString(logoText, positionWidth, positionHeight);
  144. // 9、释放资源
  145. g.dispose();
  146. // 10、生成图片
  147. os = new FileOutputStream(targerPath);
  148. ImageIO.write(buffImg, "JPG", os);
  149.  
  150. logger.info("图片完成添加水印文字");
  151.  
  152. } catch (Exception e) {
  153. e.printStackTrace();
  154. } finally {
  155. try {
  156. if (null != is)
  157. is.close();
  158. } catch (Exception e) {
  159. e.printStackTrace();
  160. }
  161. try {
  162. if (null != os)
  163. os.close();
  164. } catch (Exception e) {
  165. e.printStackTrace();
  166. }
  167. }
  168. }
  169.  
  170. /**
  171. * 判断文件是不是图片
  172. * @param file
  173. * @return
  174. * @author guogf
  175. */
  176. public static boolean isImage(File file)
  177. {
  178. boolean flag = false;
  179. try
  180. {
  181. Image is = ImageIO.read(file);
  182. if(null != is)
  183. {
  184. flag = true;
  185. }
  186. } catch (Exception e)
  187. {
  188. e.printStackTrace();
  189. }
  190. return flag;
  191. }
  192.  
  193. }

关于编辑器的前台配置

到此后台处理函数已完整实现,现在需要将后台返回的json串回调到前端编辑器来处理,写入上传容器框以及编辑器文章当中。

首先去找到umeditor.config.js 找到如下并修改:

  1. //图片上传配置区
  2. ,imageUrl:ctx+"/cms/article/upload" //图片上传提交地址 很重要!上传提交地址,这里配置成我刚才写的那个控制器函数 路径很重要 ctx获取的是当前域名
  3. ,imagePath:"http://static.xx.com/" //图片修正地址,对应刚才我提到的域名配置的地方 必须配置!!
  4. ,imageFieldName:"upfile" //图片数据的key,若此处修改,需要在后台对应文件修改对应参数 对应刚才提到的上传图片的表单name 默认即可

配置好之后,后来调试发现 返回的json 串包含在pre标签里,编辑器脚本解析不了,报错,所以,这里还需要修改编辑器目录 dialogs\image\image.js文件

  1. uploadComplete: function(r){
  2. debugger;
  3. var me = this;
  4. try{
  5. var json = eval('('+$(r).html()+')'); //这里需要把返回的字符串转化成对象 pre对象,然后获取里面json串
  6. Base.callback(me.editor, me.dialog, json.url, json.state);
  7. }catch (e){
  8. var lang = me.editor.getLang('image');
  9. Base.callback(me.editor, me.dialog, '', (lang && lang.uploadError) || 'Error!');
  10. }
  11. },

好了,到这里基本完事了,我们看下最终效果吧!

开始上传效果图:

  拖曳或者上传效果图:

编辑器效果图:

文章预览效果图:

说在后面

这篇博文写了挺长时间,手都酸了...之前有部分分享总是被移除首页,所以,在格式排版上花很长时间..

如果,您认为阅读这篇博客让您有些收获,不妨点击一下右下角的【推荐】按钮。
如果,您希望更容易地发现我的新博客,不妨点击一下绿色通道的【关注我】。

如果,想给予我更多的鼓励,求打赏

  
欢迎资助我持续写作,金额随意,欢迎来赏!

因为,我的写作热情也离不开您的肯定支持。

感谢您的阅读,如果您对我的博客所讲述的内容有兴趣,请继续关注我的后续博客 。

另外,需要声明:原创博客请在转载时保留原文链接或者在文章开头加上本人博客地址(http://www.cnblogs.com/sybboy),如发现错误,欢迎批评指正。

【完整靠谱版】结合公司项目,仔细总结自己使用百度编辑器实现FTP上传的完整过程的更多相关文章

  1. 黑马在线教育项目---34-37、webuploader实现用户头像的异步上传

    黑马在线教育项目---34-37.webuploader实现用户头像的异步上传 一.总结 一句话总结: 启迪:可以多看学习视频,在看电影看电视的时候看的确是不错的选择 1.关于软件的发行版本? 第1阶 ...

  2. app store 注册账号生成证书上传app完整的教程

    app store为开发者提供四种类型的申请: 个人ios开发者计划$99/年 公司ios开发者计划$99/年 企业ios开发者计划$299/年 高校ios开发者计划免费 在这里主要介绍一下公司ios ...

  3. 转:app store 注册账号生成证书上传app完整的教程

    app store为开发者提供四种类型的申请: 个人ios开发者计划$99/年 公司ios开发者计划$99/年 企业ios开发者计划$299/年 高校ios开发者计划免费 在这里主要介绍一下公司ios ...

  4. js仿百度文库文档上传页面的分类选择器_第二版

    仿百度文库文档上传页面的多级联动分类选择器第二版,支持在一个页面同一时候使用多个分类选择器. 此版本号把HTML,CSS,以及图片都封装到"category.js"中.解决因文件路 ...

  5. C#中富文本编辑器Simditor带图片上传的全部过程(MVC架构的项目)

    描述:最近c#项目中使用富文本编辑器Simditor,记录一下以便以后查看. 注:此项目是MVC架构的. 1.引用文件 项目中引用相应的css和js文件,注意顺序不能打乱,否则富文本编辑器不会正常显示 ...

  6. c#中富文本编辑器Simditor带图片上传的全部过程(项目不是mvc架构)

    描述:最近c#项目中使用富文本编辑器Simditor,记录一下以便以后查看. 注:此项目不是MVC架构的. 1.引用文件 项目中引用相应的css和js文件,注意顺序不能打乱,否则富文本编辑器不会正常显 ...

  7. FTP上传下载文件(函数简易版)

    FTP上传下载文件(函数简易版) # 服务端 import socket import json import hashlib import struct import os user_dic = { ...

  8. 一步步开发自己的博客 .NET版(4、文章发布功能)百度编辑器

    前言 这次开发的博客主要功能或特点: 第一:可以兼容各终端,特别是手机端. 第二:到时会用到大量html5,炫啊. 第三:导入博客园的精华文章,并做分类.(不要封我) 第四:做个插件,任何网站上的技术 ...

  9. 百度编辑器UEditor与UEditor 公式插件完整Demo

    1.下载UEditor(我的是.net项目) 2.下载UEditor公式插件 3.新建解决方案和项目 4.在浏览器中预览index.html页面 结果: 5.index.html源码 <!DOC ...

随机推荐

  1. javascript数据结构-链表

    gihtub博客地址 链表 是一种物理存储单元上非连续.非顺序的存储结构,它既可以表示线性结构,也可以用于表示非线性结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的.链表由一系列结点(链表中每 ...

  2. phpcms手机门户相关

    相关标签 {$WAP['sitename']}标题 {list_url(3)} 调取栏目链接 {template "wap","header"}{templat ...

  3. Nubia Z9 mini使用体验

    前续用的手机:荣耀6 想换的理由: 1, 充电不方便,除了原装的充电器和小米移动电源,其他的充电器和移动电源约有一半都只能以USB方式慢充,即使是2.0A以上输出电流的: 2, 拍照太渣. Z9 mi ...

  4. lua高阶用法 OO的实现

    //Lua的类的实现,可以派生,可重写方法 local _class={} function class(super) local class_type={} class_type.ctor=fals ...

  5. c/c++ string.h

    操作数的都是 ( char * )型,操作时不考虑末尾的'\0',操作数必须是指向字符串的指针("a"),不能是字符('a'). size_t strlen( const char ...

  6. css中选择器的使用技巧

    td:first-child{选择第一个} td:last-child{选择最后一个} td:nth-child(3){选择第3个} 一个简单的选择方法,很方便

  7. 【Javascript】解决Ajax轮询造成的线程阻塞问题(过渡方案)

    一.背景 开发Web平台时,经常会需要定时向服务器轮询获取数据状态,并且通常不仅只开一个轮询,而是根据业务需要会产生数个轮询.这种情况下,性能低下的Ajax长轮询已经不能满足需求,频繁的访问还会造成线 ...

  8. java中的权限修饰符的理解

    首先了解概念: 在java中有四种权限修饰符:范围从大到小分别是:public.protect.default(friendly).private,它们之间的区别是: public: Java语言中访 ...

  9. PHP运行模式

    1.运行模式 关于PHP目前比较常见的五大运行模式: 1)CGI(通用网关接口 / Common Gateway Interface) 2)FastCGI(常驻型CGI / Long-Live CGI ...

  10. Leetcode Copy List with Random Pointer

    A linked list is given such that each node contains an additional random pointer which could point t ...