1. 程式人生 > >JavaMail傳送郵件後再通過JavaMail接收格式問題

JavaMail傳送郵件後再通過JavaMail接收格式問題

複雜郵件傳送問題

關於 JavaMail 如何傳送郵件這裡就不贅述了,網上有很多例子。其中最複雜的郵件傳送莫過於 html郵件包含內嵌圖片以及附件,最近專案中的這項功能我在傳送郵件時就出現了一系列問題。

我在使用 JavaMail 傳送了郵件之後,會再次通過 JavaMail 將其獲取回來進行解析,由於傳送操作不當,導致瞭解析就不是那麼回事了。

接下來先看看正常的解析過程吧。關於郵件的解析,網上依然有很多例子。

private static void multipartMailParser(Multipart mail) throws Exception {
    int count = mail.getCount();
    System.out.println("part count: " + count);
    for (int i = 0; i < count; i++) {
        BodyPart bodyPart = mail.getBodyPart(i);

        //cid 解析
        //cid 示例: <4a85b9c9$1$16af704cfc3$Coremail$user_taohan$163.com>
        String[] cids = bodyPart.getHeader("Content-Id");
        System.out.println("=========> cids: " + (cids == null ? "NULL" : cids.length));
        String content = "", cid = "";
        if (cids != null && cids.length > 0) {
            content = cids[0];
            if (content.startsWith("<") && content.endsWith(">")) {
                cid = "cid:" + content.substring(1, content.length() - 1);
            } else {
                cid = "cid:" + content;
            }
        }
        System.out.println(content+"---"+cid);

        System.out.println(bodyPart.getContentType());
        if (bodyPart.isMimeType("text/plain")) {
            System.out.println("純文字郵件: " + bodyPart.getContent());
        } else if (bodyPart.isMimeType("text/html")) {
            System.out.println("html郵件: " + bodyPart.getContent());
        } else if (bodyPart.isMimeType("multipart/*")) {
            Multipart part = (Multipart)bodyPart.getContent();
            multipartMailParser(part);
        } else if (bodyPart.isMimeType("application/octet-stream")) {
            String disposition = bodyPart.getDisposition();
            System.out.println("任意的二進位制資料: " + disposition);
            //這裡就是對附件進行解析
            if (disposition.equalsIgnoreCase(BodyPart.ATTACHMENT)) {
                String fileName = bodyPart.getFileName();
                System.out.println("------------------------儲存附件 " + fileName);
                InputStream is = bodyPart.getInputStream();
                File file = new File("C:\\Users\\AB\\Desktop\\mail\\"+fileName);
                copy(is, new FileOutputStream(file));
            }
        } else if (bodyPart.isMimeType("image/*") && !("".equals(cid))) {
            //內嵌圖片處理
            DataHandler dataHandler = bodyPart.getDataHandler();
            String name = dataHandler.getName();
            System.out.println("內嵌圖片 NAME: " + name);
            InputStream is = dataHandler.getInputStream();
            File file = new File("C:\\Users\\AB\\Desktop\\mail\\"+name);
            copy(is, new FileOutputStream(file));
        }
    }
}


private static void copy(InputStream is, OutputStream os) throws IOException {
    byte[] bytes = new byte[1024];
    int len = 0;
    while ((len=is.read(bytes)) != -1 ) {
        os.write(bytes, 0, len);
    }
    if (os != null)
        os.close();
    if (is != null)
        is.close();
}

 

使用以上方法對郵件進行解析是沒有問題的,內嵌圖片、附件都是可以分開解析的。但在我解析通過 JavaMail 傳送的郵件時就出現了問題。

先看看最初是怎麼傳送的吧。這裡就不貼完整程式碼了,我就是按照文章開始連結對應的文章進行了修改。

//這裡只給出附件節點建立方法吧
//給出引數accessory(附件資訊)格式為: zxd.jpg-C:/Users/AB/Desktop/zxd.jpg,lyf.jpg-C:/Users/AB/Desktop/lyf.jpg,htmlFile-C:/Users/AB/Desktop/file.html,golang-C:/Users/AB/Desktop/Demo.go
private  List<MimeBodyPart> mailAttachmentParts(String accessory) throws MessagingException, UnsupportedEncodingException {
    //附件節點集合
    List<MimeBodyPart> attachments = new ArrayList<>();
    MimeBodyPart attachment;
    DataHandler dh;
    String[] accs = accessory.split(",");
    for (final String acc : accs) {
        String[] ac = acc.split("-");
        //按照網上文章的例子,這裡只需要進行如下設定即可
        attachment = new MimeBodyPart();
        dh = new DataHandler(new FileDataSource(ac[1]));
        attachment.setDataHandler(dh);
        attachments.add(attachment);
    }

    return attachments;
}

 

傳送後,檢視去郵件伺服器中檢視,郵件是正常的。但是我再通過 JavaMail 獲取就出現問題了。輸出如下:(//…​為我給出的註釋)

part count: 5
=========> cids: NULL
---
multipart/related;
    boundary="----=_Part_2_1562389956.1559641692502"
part count: 2
=========> cids: NULL
---
text/html; charset=UTF-8
//郵件內容正常獲取
html郵件: <html><body><h1>這是郵件傳送測試十二</h1><b>這依然是劉亦菲</b><br><br><br><img src='cid:liuyifei' /></body></html>

//內嵌圖片也正常獲取
=========> cids: 1
liuyifei---cid:liuyifei
image/jpeg; name=lyf2.jpg
內嵌圖片 NAME: lyf2.jpg
=========> cids: NULL
---
image/jpeg; name=zxd.jpg   //附件圖片獲取失敗, 可以看見前面為 image/jpeg,也就是說 JavaMail 並沒有將其作為附件進行處理
=========> cids: NULL
---
image/jpeg; name=lyf.jpg   //附件圖片獲取失敗
=========> cids: NULL
---
//最可笑的是居然將我的 file.html 檔案當做了 text/html 來進行了處理, 沒有將 html 檔案儲存到本地,而是直接輸出了其中的內容
//我在手機郵件app中檢視這封郵件的時候, 我的郵件內容並不是上面列印的內容, 而是這個 html 檔案中的內容
text/html; charset=us-ascii; name=file.html
html郵件: <!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>file upload</title>
</head>
<body>
    <div>
        <form action="http://localhost:8080/api/mail/record/accessory/upload" method="POST" enctype="multipart/form-data">
            <input type="file" name="file">
            <input type="submit" value="Submit">
        </form>
    </div>
</body>
</html>

//但奇怪的是 Demo.go 這個檔案又被正常的當做了附件處理
//到這裡就大概知道其中的原因了
//上面的 圖片和html 檔案都是比較特殊的, 但是 Demo.go 就不一樣了
=========> cids: NULL
---
application/octet-stream; name=Demo.go
任意的二進位制資料: attachment
------------------------儲存附件 Demo.go

於是,我將需要傳送的郵件儲存至本地, message.writeTo(new FileOutputStream("D/mail.eml")) 即可;

開啟檢視其中的內容發現

附件都有如下內容:

Content-Type: image/jpeg; name=lyf.jpg
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename=lyf.jpg
—————————————————————
Content-Type: application/octet-stream; name=Demo.go
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename=Demo.go

而對比上面獲取郵件的輸出內容,只有 Demo.go 這一個檔案輸出了 application/octet-stream 字樣, 所以問題就已經變得很明顯了。

只需要在建立附件節點時,為附件節點設定以下幾樣東西即可

attachment.setFileName(MimeUtility.encodeText(dh.getName())); //附件名稱
attachment.setDisposition(BodyPart.ATTACHMENT);  //設定 disposition 為 attachment (附件標識)
attachment.setHeader("content-disposition", "attachment; filename="+dh.getName());  //設定以下兩個 header
attachment.setHeader("content-type", "application/octet-stream; name="+dh.getName());

再看獲取郵件輸出:

part count: 5
=========> cids: NULL
---
multipart/related;
    boundary="----=_Part_2_1714832523.1559700934372"
part count: 2
=========> cids: NULL
---
text/html; charset=UTF-8
html郵件: <html><body><h1>這是郵件傳送測試十四</h1><b>這依然是劉亦菲</b><br><br><br><img src='cid:liuyifei' /></body></html>

=========> cids: 1
liuyifei---cid:liuyifei
image/jpeg; name=lyf2.jpg
內嵌圖片 NAME: lyf2.jpg
=========> cids: NULL
---
application/octet-stream; name=zxd.jpg
任意的二進位制資料: attachment
------------------------儲存附件 zxd.jpg
=========> cids: NULL
---
application/octet-stream; name=lyf.jpg
任意的二進位制資料: attachment
------------------------儲存附件 lyf.jpg
=========> cids: NULL
---
application/octet-stream; name=file.html
任意的二進位制資料: attachment
------------------------儲存附件 file.html
=========> cids: NULL
---
application/octet-stream; name=Demo.go
任意的二進位制資料: attachment
------------------------儲存附件 Demo.go

這下就正常了,其實解決辦法就是新增上面幾樣配置即可。