springboot新增郵件傳送及檔案壓縮功能
轉載請註明出處:https://www.cnblogs.com/funnyzpc/p/9190233.html
先來一段詩
```
就這樣吧
忍受折磨
然後,躺進醫院
然後,死去
化作一抔土
從此,這世界沒有煩惱
沒有病痛
沒有我
也沒有這個世界
```
以上是在半睡半醒中想到的,寫的不好,讀者可直接略過。
這次本來只講講郵件傳送功能的,憚於內容比較貧乏,故加了點兒檔案壓縮的功能講解。
首先郵件傳送,郵件功能在springboot裡面是有對應的依賴元件,這個:
1 <dependency>
2 <groupId>org.springframework.boot</groupId>
3 <artifactId>spring-boot-starter-mail</artifactId>
4 </dependency>
郵件功能開發在springboot裡面相當簡單,這裡我大致總結下開發內容:
A>新增依賴包
B>配置Mail基本引數(ymal或propertie裡面)
C>Service中注入JavaMailSender,呼叫相關方法即可
但是這裡面可能會有個問題,就是在具體伺服器部署的時候伺服器會封堵郵件服務埠,以及普通郵件安全問題,這裡講解的時候我會順道給出解決之道。
首先,需要在工程的pom.xml中引入郵件元件,元件的版本需對應springboot的版本(可不寫,這裡我略去):
1 <dependency> 2 <groupId>org.springframework.boot</groupId> 3 <artifactId>spring-boot-starter-mail</artifactId> 4 </dependency>
接下來就是在配置檔案中配置郵件的基本引數:
1 spring:
2 mail:
3 host: smtp.exmail.qq.com
4 username: username@hostname.com
5 password: 密碼
6 default-encoding: UTF-8
7 ssl:
8 trust: smtp.exmail.qq.com
9 properties:
10 mail:
11 smtp:
12 auth: true #是否需要認證
13 socketFactory:
14 class: javax.net.ssl.SSLSocketFactory #SSL證照Socket工廠
15 port: 465 #使用SMTP465埠
配置引數的時候一定要注意縮排,因為我給的是yaml的配置格式,若是properties配置,大致是這樣子(例子):spring.mail.host:smtp.exmail.qq.com,每一個子項都是完整的格式,一開始我是省略了properties項以下的配置(是否認真,SSL,埠),後來發現伺服器將郵件的25埠封了,所以在本地可以但是在伺服器就行不通了,所以需要指定郵件服務埠為465,我這裡使用的是qq郵箱,如果使用163或其他郵箱需自行查閱服務商支援的埠,至於郵件安全問題,在這裡需要宣告兩個,一個是ssl信任,以及mail的socket工廠,具體請見以上紅色部分,以上配置僅對qq郵箱有效,不保證其他郵箱也適用。
ok,配置完成,這裡就開始寫具體的實現類:
1 import XXX.common.util.DateUtil; 2 import org.apache.commons.lang3.StringUtils; 3 import org.slf4j.Logger; 4 import org.slf4j.LoggerFactory; 5 import org.springframework.beans.factory.annotation.Autowired; 6 import org.springframework.beans.factory.annotation.Value; 7 import org.springframework.mail.SimpleMailMessage; 8 import org.springframework.mail.javamail.JavaMailSender; 9 import org.springframework.mail.javamail.MimeMessageHelper; 10 import org.springframework.stereotype.Service; 11 12 import javax.mail.internet.MimeMessage; 13 import java.util.Date; 14 import java.util.List; 15 import java.util.Map; 16 17 @Service 18 public class MailService { 19 private static final Logger LOG = LoggerFactory.getLogger(MailService.class); 20 21 @Value("${spring.mail.username}") 22 private String SEND_USER_ADDR; 23 24 @Autowired 25 private JavaMailSender mailSender; 26 27 /** 28 * 傳送簡單郵件 29 * @param receive 收件人 30 * @param obj 傳送主題 31 * @param content 郵件內容 32 */ 33 public void sendSimpleMail(String receive,String obj,String content) { 34 if(!StringUtils.isNotBlank(content) || !StringUtils.isNotBlank(receive)) 35 return;//不傳送空郵件 36 SimpleMailMessage message = new SimpleMailMessage(); 37 message.setFrom(SEND_USER_ADDR); 38 if(receive.contains(";")) 39 message.setTo(receive.split(";")); 40 else 41 message.setTo(receive); 42 message.setSubject(obj); 43 message.setText(content); 44 try { 45 mailSender.send(message); 46 LOG.info("Simple mail send success!"); 47 } catch (Exception e) { 48 LOG.error("sendSimpleMail ERROR!", e); 49 } 50 51 } 52 53 private StringBuilder strBuilder; 54 /** 55 * 傳送html郵件 多列表單的形式 56 * @param receive 收件人 57 * @param obj 傳送主題(題目) 58 * @param content 郵件內容 59 */ 60 public void sendHtmlMailByList(String receive,String obj,List<Map> content){ 61 if(content.isEmpty() || !StringUtils.isNotBlank(receive) || null==obj) 62 return; 63 MimeMessage msg = mailSender.createMimeMessage(); 64 try { 65 MimeMessageHelper helper = new MimeMessageHelper(msg, true, "UTF-8"); //解決亂碼問題 66 helper.setFrom(SEND_USER_ADDR); 67 if(receive.contains(";")) 68 helper.setTo(receive.split(";")); 69 else 70 helper.setTo(receive); 71 helper.setSubject(obj); 72 strBuilder=new StringBuilder(); 73 strBuilder.append("<!DOCTYPE html><html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"></head><body style=\"padding:3% 2%;\">"); 74 strBuilder.append("<h2>This message is automatically sent to the system.</h2>"); 75 strBuilder.append("<h2>Send Date by "+DateUtil.getDateFormat(new Date(),DateUtil.DATETIME_DEFAULT_FORMAT) +"</h2>"); 76 strBuilder.append("<h2>The following is the details:</h2>"); 77 strBuilder.append("<table border=\"2px solid red\" width=\"100%\">"); 78 79 //頭 80 strBuilder.append("<thead style=\"background-color: #aea2e2;\">"); 81 strBuilder.append("<tr>"); 82 Object[] st=content.get(0).keySet().toArray(); 83 for(int i=0;i<st.length;i++) 84 strBuilder.append("<th>"+st[i]+"</th>"); 85 strBuilder.append("</tr>"); 86 strBuilder.append("</thead>"); 87 88 //體 89 strBuilder.append("<tbody>"); 90 for(Map item:content){ 91 strBuilder.append("<tr>"); 92 for(Object str:st) 93 strBuilder.append("<td>"+item.get(str)+"</td>"); 94 strBuilder.append("</tr>"); 95 } 96 strBuilder.append("</tbody>"); 97 98 strBuilder.append("</table>"); 99 strBuilder.append("<h3 style=\"text-align:right\">Best wishes</h3>"); 100 strBuilder.append("</body></html>"); 101 //LOG.info(strBuilder.toString()); 102 helper.setText(strBuilder.toString(),true); 103 }catch (Exception e){ 104 LOG.error("sendHtmlMail ERROR:",e); 105 } 106 mailSender.send(msg); 107 } 108 109 110 /** 111 * 傳送html郵件 單列記錄形式 112 * @param receive 收件人 113 * @param obj 傳送主題(題目) 114 * @param content 郵件內容 115 */ 116 public void sendHtmlMailByItem(String receive,String obj,List<String> content){ 117 if(content.isEmpty() || !StringUtils.isNotBlank(receive) || null==obj) 118 return; 119 MimeMessage msg = mailSender.createMimeMessage(); 120 try { 121 MimeMessageHelper helper = new MimeMessageHelper(msg, true, "UTF-8"); //解決亂碼問題 122 helper.setFrom(SEND_USER_ADDR); 123 if(receive.contains(";")) 124 helper.setTo(receive.split(";")); 125 else 126 helper.setTo(receive); 127 helper.setSubject(obj); 128 strBuilder=new StringBuilder(); 129 strBuilder.append("<!DOCTYPE html><html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"></head><body style=\"padding:3% 2%;\">"); 130 strBuilder.append("<h3>This message is automatically sent to the system.</h3>"); 131 strBuilder.append("<h3>Send Date by "+DateUtil.getDateFormat(new Date(),DateUtil.DATETIME_DEFAULT_FORMAT) +"</h3>"); 132 strBuilder.append("<h3>The following is the details:</h3>"); 133 strBuilder.append("<table border=\"2px solid red\" width=\"100%\">"); 134 135 //頭 136 strBuilder.append("<thead style=\"background-color: #aea2e2;\">"); 137 138 strBuilder.append("<th>"+obj.toUpperCase()+" DETAIL</th>"); 139 strBuilder.append("</thead>"); 140 141 //體 142 strBuilder.append("<tbody>"); 143 for(String item:content){ 144 strBuilder.append("<tr><td>"+item+"</td></tr>"); 145 } 146 strBuilder.append("</tbody>"); 147 148 strBuilder.append("</table>"); 149 strBuilder.append("<h3 style=\"text-align:right;font-weight:normal;\">Best wishes</h3>"); 150 strBuilder.append("</body></html>"); 151 LOG.info(strBuilder.toString()); 152 helper.setText(strBuilder.toString(),true); 153 }catch (Exception e){ 154 LOG.error("sendHtmlMail ERROR:",e); 155 } 156 mailSender.send(msg); 157 } 158 }
以上我是將郵件功能封裝成一個服務類,使用的時候只需要將當前類注入 然後直接呼叫即可,以上封裝了兩個方法:一個是簡單郵件傳送,一個是帶html table的郵件,如果需要傳送附件,需將附件放入到MimeMessageHelper裡面(呼叫addAttachment("檔名", 檔案))方法即可,這裡因為無實際需求,遂就略去了,好了,郵件傳送功能已經完成,這裡看下實際效果:
郵件功能實現完畢,現在我講講檔案壓縮功能,壓縮功能的實現大致有四種,分別是:
A>利用java.util.zip提供的api壓縮
B>利用apache的ant包提供的api壓縮(org.apache.tools.ant.taskdefs.Zip)
C>使用zip4j提供的api壓縮(net.lingala.zip4j)
D>呼叫宿主機的shell命令壓縮
這裡需要特別提到三個問題:
A>普通郵件壓縮中文亂碼(不支援中文)
B>壓縮後無法解壓(解壓錯誤)
C>檔案壓縮新增壓縮密碼問題
實際開發過壓縮功能,以上三點兒對於新手來說尤其的頭痛,這裡我分享下以前在開發壓縮功能中碰到的問題。
使用原生java.util包提供的壓縮,如果被壓縮檔案使用到中文,則會亂碼(據說是jdk的一個bug),而且壓縮實現的程式碼較為複雜(尤其是設定密碼),尤其是對於跨目錄壓縮和多檔案壓縮尤其麻煩。
使用apache提供的zip工具雖避免了以上會出現的問題,但是需要提醒一點兒的是這個ant包與webLogic衝突(部署的時候會報錯)且無法實現壓縮設定密碼,如果使用的是webLogic而不是tomocat的情況下,一定要注意到這個問題。
使用java呼叫宿主機的shell命令也是個不錯的選擇,但是,需要編寫shell命令,同時對於部署在windows平臺就不太友好了,移植比較麻煩。
最後,對於以上問題,我這裡推薦zip4j,以下也是針對zip4j的壓縮實現做講解。
先,需要引入依賴包:
1 <!--壓縮:支援加密壓縮--> 2 <dependency> 3 <groupId>net.lingala.zip4j</groupId> 4 <artifactId>zip4j</artifactId> 5 <version>1.3.2</version> 6 </dependency>
再,封裝一個壓縮/解壓縮工具類以方便使用:
1 import net.lingala.zip4j.core.ZipFile; 2 import net.lingala.zip4j.exception.ZipException; 3 import net.lingala.zip4j.model.ZipParameters; 4 import net.lingala.zip4j.util.Zip4jConstants; 5 import org.springframework.util.StringUtils; 6 7 import java.io.File; 8 9 10 /** 11 * 本工具類使用Zip4j來進行壓縮以及解壓縮 12 */ 13 public class ZipUtil { 14 15 //宣告壓縮物件 16 private static ZipParameters parameters; 17 18 //解壓檔案物件 19 private static ZipFile zipFile; 20 21 /** 22 * 23 * @param sourceFilePath 被壓縮的檔案的路徑(單檔案,資料夾) 24 * @param zipFilePath 壓縮檔案路徑 25 * @param password 壓縮密碼 26 * @return 壓縮成功:true ,壓縮失敗:false 27 */ 28 public static Boolean singleFileCompress(String sourceFilePath,String zipFilePath,String password){ 29 parameters = new ZipParameters(); 30 parameters.setCompressionMethod(Zip4jConstants.COMP_DEFLATE); // 壓縮方式(預設方式) 31 parameters.setCompressionLevel(Zip4jConstants.DEFLATE_LEVEL_NORMAL); // 壓縮級別(預設級別) 32 //壓縮加密設定 33 if (!StringUtils.isEmpty(password)) { 34 parameters.setEncryptFiles(true);//是否設定檔案加密(預設為否) 35 parameters.setEncryptionMethod(Zip4jConstants.ENC_METHOD_STANDARD); // 加密方式(此處是標準壓縮) 36 parameters.setPassword(password.toCharArray()); 37 } 38 try { 39 ZipFile zipFile = new ZipFile(zipFilePath); 40 //如果是檔案則直接壓縮,若是資料夾,遍歷檔案全部壓縮 41 if(new File(sourceFilePath).isFile()) { 42 zipFile.setFileNameCharset("GBK"); 43 zipFile.addFile(new File(sourceFilePath), parameters); 44 return true; 45 } 46 //File ff=new File(sourceFilePath); 47 File[] flst=new File(sourceFilePath).listFiles(); 48 System.out.println("檔案個數=>"+flst.length); 49 for(File f:flst){ 50 zipFile.setFileNameCharset("GBK"); 51 zipFile.addFile(f, parameters); 52 } 53 54 return true; 55 } catch (ZipException e) { 56 e.printStackTrace(); 57 return false; 58 }catch (Exception id){ 59 id.printStackTrace(); 60 return false; 61 } 62 } 63 public static Boolean unZip(String zipFile,String unZipDir){ 64 try { 65 ZipUtil.zipFile = new ZipFile(zipFile); 66 ZipUtil.zipFile.setFileNameCharset("GBK");//設定編碼格式 67 //用自帶的方法檢測一下zip檔案是否合法,包括檔案是否存在、是否為zip檔案、是否被損壞等 68 if (!ZipUtil.zipFile.isValidZipFile()) { 69 throw new ZipException("檔案不合法或不存在"); 70 } 71 // 跟java自帶相比,這裡檔案路徑會自動生成,不用判斷 72 ZipUtil.zipFile.extractAll(unZipDir); 73 return true; 74 }catch(ZipException e){ 75 return false; 76 } 77 } 78 }
以上壓縮方法自帶密碼壓縮功能,可以壓縮單檔案也可以壓縮目錄檔案,相對於原生的實現,一下子清爽了許多,這裡唯一需要說明的是,壓縮的目標檔案在壓縮前一定不能穿件,否則會報錯!另外對於解壓縮一定要注意檔案編碼和判斷檔案是否存在。
OK,本章的功能已盡數分享,希望各位在開發功能的時候能避免這其中的坑。
現在是2018-07-14 22:16:12 ,各位晚安