JavaMail 郵件傳送,有意思的附件名亂碼 → 客戶端正常,web端亂碼

青石路發表於2023-03-08

開心一刻

  昨晚,媳婦很感傷的看著我

  媳婦:以後歲數大了,我要走你前面去了,你再找個老伴

  我:我不想找

  媳婦:你找一個,不用替我守著,以後你說你頭疼發燒,也得有個給你端水遞藥的呀

  媳婦抹著眼淚:到老是個伴

  我:我想找個年輕的

  現在我左臉還有一個掌印,火辣辣的

問題背景

  基於 JavaMail 1.5.5 ,實現了郵件傳送功能,也對接了一些客戶,沒出現什麼問題

  程式碼如下

JavaMail 郵件傳送,有意思的附件名亂碼 → 客戶端正常,web端亂碼
/**
 * 郵件傳送
 * @param message 郵件內容
 * @param to 收件人郵箱
 * @param attachment 附件
 */
public static void sendEmail(String message, String to, File attachment) 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);

    Multipart multipart = new MimeMultipart();
    BodyPart contentPart = new MimeBodyPart();
    //contentPart.setContent(message, "text/html;charset=UTF-8");
    contentPart.setText(message);
    multipart.addBodyPart(contentPart);

    if (attachment != null) {
        BodyPart attachmentBodyPart = new MimeBodyPart();
        DataSource source = new FileDataSource(attachment);
        attachmentBodyPart.setDataHandler(new DataHandler(source));
        //MimeUtility.encodeWord可以避免附件檔名亂碼
        attachmentBodyPart.setFileName(MimeUtility.encodeWord(attachment.getName()));
        multipart.addBodyPart(attachmentBodyPart);
    }

    //透過會話,得到一個郵件,用於傳送
    Message msg = new MimeMessage(session);
    //設定發件人
    msg.setFrom(new InternetAddress(MAIL_USER_NAME));
    //設定收件人,to為收件人,cc為抄送,bcc為密送
    msg.setRecipients(Message.RecipientType.TO, InternetAddress.parse(to, false));
    // msg.setRecipients(Message.RecipientType.CC, InternetAddress.parse(to, false));
    // msg.setRecipients(Message.RecipientType.BCC, InternetAddress.parse(to, false));
    msg.setSubject("我是主題");
    //設定郵件訊息
    msg.setContent(multipart);
    //設定傳送的日期
    msg.setSentDate(new Date());

    //呼叫Transport的send方法去傳送郵件
    Transport.send(msg);
}
View Code

  附件名是做了編碼處理的

  我們來看下接收情況

  Foxmail

  outlook windows 版本

  一切看似都很平靜

JavaMail 郵件傳送,有意思的附件名亂碼 → 客戶端正常,web端亂碼

 

  直到她們的出現,讓我慌了神

  QQ郵箱(web 端)

  outlook web 版本

  此刻,我們的腦中應該有 2 個問題

  1、亂碼該如何修復

  2、為什麼客戶端版(Foxmail、outlook windows版)接收正常,而 web版 卻出現了亂碼?

亂碼處理

  這個上網一搜,很容易就能找到答案,加一個系統屬性即可

   mail.mime.splitlongparameters 預設值是 true ,表示編碼後的附件名檔名長度超過 60 之後會進行多段拆分,每 60 個字元作為一個引數,最後不足 60 個字元的作為一個引數

   我們把 mail.mime.splitlongparameters 設定成 false ,再看下效果

  QQ 郵箱

   outlook web

  有人可能會有疑問了:你說 60 就 60,你說拆分就拆分?

  既然不信我,那我們從原始碼找答案

  原始碼解析

JavaMail 郵件傳送,有意思的附件名亂碼 → 客戶端正常,web端亂碼

  設定附件名的時候,有這樣一段程式碼

  注意第一個 if 中的條件,是有三個

    1、附件名編碼後的長度

    2、 mail.mime.splitlongparameters 

    3、 mail.mime.encodeparameters ,預設值是 true 

  當三個條件都為 true ,才會以 60 字元為單位進行多段拆分

   你好_好久不見_別來無恙_20230306.txt 編碼後再拆分得到的結果是

  檔名被拆分成了三段,我可曾欺你們?

JavaMail 郵件傳送,有意思的附件名亂碼 → 客戶端正常,web端亂碼

為什麼只有 web 版“亂碼”

  此刻需要糾正下,web 版出現的附件名不是亂碼,而是編碼之後未能正確解碼

  為什麼未能正確解碼?

  那是因為不支援 RFC2231 style encoded parameters 

  其實可能不只是 web 版不支援,可能還有其他的郵件客戶端不支援,只是樓主未去嘗試而已

總結

  1、是要滿足三個條件才會對附件名進行多段拆分,忘記了的往上翻一翻

  2、為什麼要進行附件名的多段拆分? 呃呃呃...,由你們來回答

相關文章