開心一刻
昨晚,老婆輔導女兒寫作業
有一道形容媽媽的題,女兒寫下了:我媽媽像一個暴躁的老虎
老婆拿起題冊輕輕敲了下女兒,生氣到:有這麼形容你媽的嗎
女兒:你看你現在
老婆:我有那麼暴躁嗎,你就不能說我媽媽像一個公主,溫柔大方漂亮?
女兒:題目讓我造句,沒讓我造謠!
我:哈哈哈哈!
郵件傳送
基於 JavaMail 很容易實現郵件傳送,例如基於 1.5.5
傳送簡單正文
/** * 傳送簡單正文,並顯示暱稱 * @param content 正文 * @param to 收件人 * @throws Exception */ public static void sendMailNick(String content, String to) throws Exception { //設定郵件會話引數 Properties props = new Properties(); //郵箱的傳送伺服器地址 props.setProperty("mail.smtp.host", MAIL_HOST); props.setProperty("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory"); props.setProperty("mail.smtp.socketFactory.fallback", "false"); props.put("mail.smtp.ssl.enable", "true"); //郵箱傳送伺服器埠,這裡設定為465埠 props.setProperty("mail.smtp.port", "465"); props.setProperty("mail.smtp.socketFactory.port", "465"); props.put("mail.smtp.auth", "true"); //獲取到郵箱會話,利用匿名內部類的方式,將傳送者郵箱使用者名稱和密碼授權給jvm Session session = Session.getDefaultInstance(props, new Authenticator() { @Override protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication(MAIL_USER_NAME, MAIL_AUTH_CODE); } }); // 開啟除錯 session.setDebug(true); // 建立傳輸物件 Transport trans = session.getTransport(); trans.connect(MAIL_HOST, "青石路", MAIL_AUTH_CODE); // 建立郵件訊息物件 Message message = new MimeMessage(session); // 設定發件人資訊(暱稱:青石路) message.setFrom(new InternetAddress(MAIL_USER_NAME, "青石路", "UTF-8")); // 設定收件人資訊 message.addRecipient(Message.RecipientType.TO, new InternetAddress(to)); // 設定正文 Multipart multipart = new MimeMultipart(); BodyPart contentPart = new MimeBodyPart(); contentPart.setContent(content, "text/html;charset=UTF-8"); multipart.addBodyPart(contentPart); // 設定郵件主題和內容資訊 message.setSubject("暱稱測試"); message.setContent(multipart); // 傳送郵件 trans.sendMessage(message, message.getAllRecipients()); // 關閉傳輸 trans.close(); }
需要注意的是,不同的郵箱的發件箱的埠會有不同,另外發件箱也可能是授權碼而不是發件箱登陸密碼,需要大家結合具體的郵箱伺服器來設定
不出意外的話,郵件傳送成功後,收件箱會收到一封類似如下的郵件
傳送附件
很多時候,我們傳送郵件都會帶附件
實現也很簡單
/** * 傳送郵件,帶附件 * @param content 正文 * @param to 收件人 * @param attachments 附件列表 * @throws Exception */ public static void sendMailNick(String content, String to, List<File> attachments) throws Exception { //設定郵件會話引數 Properties props = new Properties(); //郵箱的傳送伺服器地址 props.setProperty("mail.smtp.host", MAIL_HOST); props.setProperty("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory"); props.setProperty("mail.smtp.socketFactory.fallback", "false"); props.put("mail.smtp.ssl.enable", "true"); //郵箱傳送伺服器埠,這裡設定為465埠 props.setProperty("mail.smtp.port", "465"); props.setProperty("mail.smtp.socketFactory.port", "465"); props.put("mail.smtp.auth", "true"); //獲取到郵箱會話,利用匿名內部類的方式,將傳送者郵箱使用者名稱和密碼授權給jvm Session session = Session.getDefaultInstance(props, new Authenticator() { @Override protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication(MAIL_USER_NAME, MAIL_AUTH_CODE); } }); // 開啟除錯 session.setDebug(true); // 建立傳輸物件 Transport trans = session.getTransport(); trans.connect(MAIL_HOST, "青石路", MAIL_AUTH_CODE); // 建立郵件訊息物件 Message message = new MimeMessage(session); // 設定發件人資訊(暱稱:青石路) message.setFrom(new InternetAddress(MAIL_USER_NAME, "青石路", "UTF-8")); // 設定收件人資訊 message.addRecipient(Message.RecipientType.TO, new InternetAddress(to)); // 設定正文 Multipart multipart = new MimeMultipart(); BodyPart contentPart = new MimeBodyPart(); contentPart.setContent(content, "text/html;charset=UTF-8"); multipart.addBodyPart(contentPart); // 新增附件 if (Objects.nonNull(attachments) && !attachments.isEmpty()) { for (File e : attachments) { BodyPart attachmentBodyPart = new MimeBodyPart(); DataSource source = new FileDataSource(e); attachmentBodyPart.setDataHandler(new DataHandler(source)); //MimeUtility.encodeWord可以避免檔名亂碼 attachmentBodyPart.setFileName(MimeUtility.encodeWord(e.getName())); multipart.addBodyPart(attachmentBodyPart); } } // 設定郵件主題和內容資訊 message.setSubject("暱稱測試"); message.setContent(multipart); // 傳送郵件 trans.sendMessage(message, message.getAllRecipients()); // 關閉傳輸 trans.close(); }
相比 傳送簡單正文 ,只多了一丟丟程式碼
不出意外的話,郵件傳送成功後,收件箱會收到一封類似如下的郵件
附件過大
但是各大電子郵箱對附件的大小都是由限制的,具體限制大小是多少,需要去看各大電子郵箱的官方說明
例如我傳送一個 200 多M的附件
結果傳送失敗,異常資訊如下
java.net.SocketException: Connection reset by peer: socket write error at java.net.SocketOutputStream.socketWrite0(Native Method) at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:109) at java.net.SocketOutputStream.write(SocketOutputStream.java:153) at sun.security.ssl.OutputRecord.writeBuffer(OutputRecord.java:431) at sun.security.ssl.OutputRecord.write(OutputRecord.java:417) at sun.security.ssl.SSLSocketImpl.writeRecordInternal(SSLSocketImpl.java:876) at sun.security.ssl.SSLSocketImpl.writeRecord(SSLSocketImpl.java:847) at sun.security.ssl.AppOutputStream.write(AppOutputStream.java:123) at com.sun.mail.util.TraceOutputStream.write(TraceOutputStream.java:138) at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:82) at java.io.BufferedOutputStream.write(BufferedOutputStream.java:126) at com.sun.mail.util.CRLFOutputStream.write(CRLFOutputStream.java:84) at com.sun.mail.smtp.SMTPOutputStream.write(SMTPOutputStream.java:87) at com.sun.mail.util.CRLFOutputStream.write(CRLFOutputStream.java:75) at com.sun.mail.util.BASE64EncoderStream.write(BASE64EncoderStream.java:140) at javax.activation.DataHandler.writeTo(DataHandler.java:309) at javax.mail.internet.MimeBodyPart.writeTo(MimeBodyPart.java:1645) at javax.mail.internet.MimeBodyPart.writeTo(MimeBodyPart.java:961) at javax.mail.internet.MimeMultipart.writeTo(MimeMultipart.java:553) at com.sun.mail.handlers.multipart_mixed.writeTo(multipart_mixed.java:81) at javax.activation.ObjectDataContentHandler.writeTo(DataHandler.java:889) at javax.activation.DataHandler.writeTo(DataHandler.java:317) at javax.mail.internet.MimeBodyPart.writeTo(MimeBodyPart.java:1645) at javax.mail.internet.MimeMessage.writeTo(MimeMessage.java:1850) at com.sun.mail.smtp.SMTPTransport.sendMessage(SMTPTransport.java:1241) at com.qsl.MailTest.sendMailNick(MailTest.java:297) at com.qsl.MailTest.main(MailTest.java:52)
碰到這種大檔案,難道郵件就沒法傳送了嗎?
針對單個的大檔案,作為一個附件確實傳送不了
如果將單個檔案拆分成多個檔案,再以多封郵件來傳送,是不是可行了?
此時大家可能會有疑問:非壓縮檔案可以按內容進行手動拆分,壓縮檔案怎麼拆,特別是安裝檔案!
我們覺得的不可能,不代表真的不可能,所以我們要多讀書,擴充我們的知識面!
分卷壓縮
關於概念,不做介紹,大家自行去搜尋,重點給大家演示實現
藉助第三方元件: zip4j
很容易實現分卷壓縮
/** * 分卷壓縮 * @param sizeThreshold 分卷閾值,即多大進行一次分卷,單位:M * @param sourceFiles 原始檔列表 * @param destDirPath 目標目錄,將原始檔分捲到哪個目錄 * @param zipFileName 壓縮檔名 * @return 分卷檔案列表 * @throws Exception */ public static List<File> splitVolumeCompressFiles(int sizeThreshold, List<File> sourceFiles, String destDirPath, String zipFileName) throws Exception { List<File> zipFiles = new ArrayList<>(); if (Objects.isNull(sourceFiles) && sourceFiles.isEmpty()) { return zipFiles; } // 目錄不存在則建立 File dir = new File(destDirPath); if (!dir.exists()) { dir.mkdirs(); } try (ZipFile zipFile = new ZipFile(destDirPath + File.separator + zipFileName + ".zip")) { ZipParameters parameters = new ZipParameters(); parameters.setCompressionMethod(CompressionMethod.DEFLATE); parameters.setCompressionLevel(CompressionLevel.NORMAL); zipFile.createSplitZipFile(sourceFiles, parameters, true, sizeThreshold * 1024L * 1024L); List<File> splitZipFiles = zipFile.getSplitZipFiles(); if (Objects.nonNull(splitZipFiles) && !splitZipFiles.isEmpty()) { zipFiles = splitZipFiles; } } return zipFiles; }
呼叫這個方法
不出意外,在 D:/volume/ 目錄下,得到如下檔案
我們直接解壓 mysql-8.0.25-winx64.zip (其他的不用管),即可得到最初的原始檔: mysql-8.0.25-winx64.zip
郵件大附件
相信此時,大家應該知道怎麼處理了吧
先進行分卷壓縮,然後一封郵件傳送一個附件,以多封郵件的方式將最初的原始檔傳送出去
收到人收到附件後,將全部附件下載到同個目錄下,然後進行解壓即可得到最初的原始檔
其實就是將 分卷壓縮 與 傳送附件 結合起來即可
public static void main(String[] args) throws Exception { List<File> attachments = new ArrayList<>(); attachments.add(new File("D:/下載/mysql-8.0.25-winx64.zip")); // 原始檔(可以是多個)進行分卷壓縮 List<File> fileList = splitVolumeCompressFiles(20, attachments, "D:/volume", "mysql-8.0.25-winx64"); // 多封郵件進行傳送,一封一個附件 for (int i=0; i<fileList.size(); i++) { // 可以非同步傳送 sendMailNick("郵件正文", MAIL_TO, Arrays.asList(fileList.get(i)), "大檔案,分卷壓縮(" + (i+1) + "/" + fileList.size() + ")"); } } /** * 分卷壓縮 * @param sizeThreshold 分卷閾值,即多大進行一次分卷,單位:M * @param sourceFiles 原始檔列表 * @param destDirPath 目標目錄,將原始檔分捲到哪個目錄 * @param zipFileName 壓縮檔名 * @return 分卷檔案列表 * @throws Exception */ public static List<File> splitVolumeCompressFiles(int sizeThreshold, List<File> sourceFiles, String destDirPath, String zipFileName) throws Exception { List<File> zipFiles = new ArrayList<>(); if (Objects.isNull(sourceFiles) && sourceFiles.isEmpty()) { return zipFiles; } // 目錄不存在則建立 File dir = new File(destDirPath); if (!dir.exists()) { dir.mkdirs(); } try (ZipFile zipFile = new ZipFile(destDirPath + File.separator + zipFileName + ".zip")) { ZipParameters parameters = new ZipParameters(); parameters.setCompressionMethod(CompressionMethod.DEFLATE); parameters.setCompressionLevel(CompressionLevel.NORMAL); zipFile.createSplitZipFile(sourceFiles, parameters, true, sizeThreshold * 1024L * 1024L); List<File> splitZipFiles = zipFile.getSplitZipFiles(); if (Objects.nonNull(splitZipFiles) && !splitZipFiles.isEmpty()) { zipFiles = splitZipFiles; } } return zipFiles; } /** * 傳送郵件,帶附件 * @param content 正文 * @param to 收件人 * @param attachments 附件列表 * @param title 郵件標題 * @throws Exception */ public static void sendMailNick(String content, String to, List<File> attachments, String title) throws Exception { //設定郵件會話引數 Properties props = new Properties(); //郵箱的傳送伺服器地址 props.setProperty("mail.smtp.host", MAIL_HOST); props.setProperty("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory"); props.setProperty("mail.smtp.socketFactory.fallback", "false"); props.put("mail.smtp.ssl.enable", "true"); //郵箱傳送伺服器埠,這裡設定為465埠 props.setProperty("mail.smtp.port", "465"); props.setProperty("mail.smtp.socketFactory.port", "465"); props.put("mail.smtp.auth", "true"); //獲取到郵箱會話,利用匿名內部類的方式,將傳送者郵箱使用者名稱和密碼授權給jvm Session session = Session.getDefaultInstance(props, new Authenticator() { @Override protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication(MAIL_USER_NAME, MAIL_AUTH_CODE); } }); // 開啟除錯 session.setDebug(true); // 建立傳輸物件 Transport trans = session.getTransport(); trans.connect(MAIL_HOST, "青石路", MAIL_AUTH_CODE); // 建立郵件訊息物件 Message message = new MimeMessage(session); // 設定發件人資訊(暱稱:青石路) message.setFrom(new InternetAddress(MAIL_USER_NAME, "青石路", "UTF-8")); // 設定收件人資訊 message.addRecipient(Message.RecipientType.TO, new InternetAddress(to)); // 設定正文 Multipart multipart = new MimeMultipart(); BodyPart contentPart = new MimeBodyPart(); contentPart.setContent(content, "text/html;charset=UTF-8"); multipart.addBodyPart(contentPart); // 新增附件 if (Objects.nonNull(attachments) && !attachments.isEmpty()) { for (File e : attachments) { BodyPart attachmentBodyPart = new MimeBodyPart(); DataSource source = new FileDataSource(e); attachmentBodyPart.setDataHandler(new DataHandler(source)); //MimeUtility.encodeWord可以避免檔名亂碼 attachmentBodyPart.setFileName(MimeUtility.encodeWord(e.getName())); multipart.addBodyPart(attachmentBodyPart); } } // 設定郵件主題和內容資訊 message.setSubject(title); message.setContent(multipart); // 傳送郵件 trans.sendMessage(message, message.getAllRecipients()); // 關閉傳輸 trans.close(); }
郵件傳送完成後,收件人按如下方式處理即可得到原始檔
總結
1、郵件附件不僅有大小限制,還有個數限制
2、檔案皆可分卷,壓縮檔案與非壓縮檔案都可分卷