郵件發送,附件太大怎麼辦 → 那就用分捲壓縮吧

来源:https://www.cnblogs.com/youzhibing/archive/2023/10/01/17561848.html
-Advertisement-
Play Games

開心一刻 昨晚,老婆輔導女兒寫作業 有一道形容媽媽的題,女兒寫下了:我媽媽像一個暴躁的老虎 老婆拿起題冊輕輕敲了下女兒,生氣到:有這麼形容你媽的嗎 女兒:你看你現在 老婆:我有那麼暴躁嗎,你就不能說我媽媽像一個公主,溫柔大方漂亮? 女兒:題目讓我造句,沒讓我造謠! 我:哈哈哈哈! 郵件發送 基於 J ...


開心一刻

  昨晚,老婆輔導女兒寫作業

  有一道形容媽媽的題,女兒寫下了:我媽媽像一個暴躁的老虎

  老婆拿起題冊輕輕敲了下女兒,生氣到:有這麼形容你媽的嗎

  女兒:你看你現在

  老婆:我有那麼暴躁嗎,你就不能說我媽媽像一個公主,溫柔大方漂亮?

  女兒:題目讓我造句,沒讓我造謠!

  我:哈哈哈哈!

郵件發送

  基於 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();
}
View Code

  需要註意的是,不同的郵箱的發件箱的埠會有不同,另外發件箱也可能是授權碼而不是發件箱登陸密碼,需要大家結合具體的郵箱伺服器來設置

  不出意外的話,郵件發送成功後,收件箱會收到一封類似如下的郵件

  發送附件

  很多時候,我們發送郵件都會帶附件

  實現也很簡單

/**
 * 發送郵件,帶附件
 * @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();
}
View Code

  相比 發送簡單正文 ,只多了一丟丟代碼

  不出意外的話,郵件發送成功後,收件箱會收到一封類似如下的郵件

  附件過大

  但是各大電子郵箱對附件的大小都是由限制的,具體限制大小是多少,需要去看各大電子郵箱的官方說明

  例如我發送一個 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)
View Code

  碰到這種大文件,難道郵件就沒法發送了嗎?

  針對單個的大文件,作為一個附件確實發送不了

  如果將單個文件拆分成多個文件,再以多封郵件來發送,是不是可行了?

  此時大家可能會有疑問:非壓縮文件可以按內容進行手動拆分,壓縮文件怎麼拆,特別是安裝文件!

  我們覺得的不可能,不代表真的不可能,所以我們要多讀書,拓展我們的知識面

分捲壓縮

  關於概念,不做介紹,大家自行去搜索,重點給大家演示實現

  藉助第三方組件: 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;
}
View Code

  調用這個方法

  不出意外,在 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();
}
View Code

  郵件發送完成後,收件人按如下方式處理即可得到源文件

總結

  1、郵件附件不僅有大小限制,還有個數限制

  2、文件皆可分捲,壓縮文件與非壓縮文件都可分捲


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

-Advertisement-
Play Games
更多相關文章
  • 1 StyleSheet 一張 StyleSheet 由一系列 Rules 組成,這些 Rules 可以分成 2 大類: 1 Style Rule 2 At-Rule 下麵的例子展示了 Style Rule 和 At-Rule: // Style Rule div { background-colo ...
  • 代碼中在使用JUC、消息隊列、回調函數、消息中間件等提高程式性能的方式進行非同步處理時,一定要分清主次,哪些邏輯必須在主線程執行,哪些邏輯可以非同步處理。 ...
  • 前面幾天的學習,我們瞭解了Dart語言的特性(基礎語法概覽、迭代集合、非同步編程和Mixin高級特性)。今天我們深入學習Dart的變數,包括:空安全(Null safety)、變數預設值、延遲變數(late)、final變數和const常量…… ...
  • Java 21引入了兩個語言核心功能: 未命名的Java類你說 新的啟動協議:該協議允許更簡單地運行Java類,並且無需太多樣板 下麵一起來看個例子。通常,我們初學Java的時候,都會寫類似下麵這樣的 Hello World 程式: public class HelloWorld { public ...
  • 【中秋國慶不斷更】HarmonyOS對通知類消息的管理與發佈通知(上) 一、 通知概述 通知簡介 應用可以通過通知介面發送通知消息,終端用戶可以通過通知欄查看通知內容,也可以點擊通知來打開應用。 通知常見的使用場景: ​ ● 顯示接收到的短消息、即時消息等。 ​ ● 顯示應用的推送消息,如廣告、版本 ...
  • WebSocket是一種在單個TCP連接上進行全雙工通信的協議。WebSocket使得客戶端和伺服器之間的數據交換變得更加簡單,允許服務端主動向客戶端推送數據。在WebSocket API中,瀏覽器和伺服器只需要完成一次握手,兩者之間就直接可以創建持久性的連接,併進行雙向數據傳輸。 ...
  • @Styles和@Extend僅僅應用於靜態頁面的樣式復用,stateStyles可以依據組件的內部狀態的不同,快速設置不同樣式。這就是我們本章要介紹的內容stateStyles(又稱為:多態樣式)。 概述 stateStyles是屬性方法,可以根據UI內部狀態來設置樣式,類似於css偽類,但語法不 ...
  • Sentinel 簡介 Sentinel 是阿裡中間件團隊開源的,面向分散式服務架構的高可用流量防護組件,主要以流量為切入點,從限流、流量整形、熔斷降級、系統負載保護、熱點防護等多個維度來幫助開發者保障微服務的穩定性 Sentinel 提供了兩個服務組件: Sentinel 用來實現微服務系統中服務 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...