• 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

    这下就正常了,其实解决办法就是添加上面几样配置即可。

    附件中文乱码解决

    经过以上处理本以为正常了,但是附件又出现了中文乱码问题,在网上找了很多办法都不行,最终解决办法如下:

    中文乱码问题:

    网上的办法都是 bodyPart.setFileName(MimeUtility.encodeText(文件名)); 我也试过这种办法,但是未能奏效,最后通过下面的办法解决

    //自己模拟一个 MimeUtility.encodeText() 返回的字符串
    BASE64Encoder encoder = new BASE64Encoder();
    String fileName="=?UTF-8?B?"+encoder.encode(dh.getName().getBytes("UTF-8"))+"?=";
    bodyPart.setFileName(fileName);
    attachment.setDisposition(BodyPart.ATTACHMENT);  //设置 disposition 为 attachment (附件标识)
    attachment.setHeader("content-disposition", "attachment; filename="+fileName);  //设置以下两个 header, 这里也需要设置 base64 编码的文件名, JavaMail 默认就是使用的Base64编码文件名
    attachment.setHeader("content-type", "application/octet-stream; name="+fileName);

    获取的时候任然要使用 MimeUtility.decodeText(bodyPart.getFileName()) 对文件名进行解码

    中文乱码过长问题:

    这个的解决办法和网上方法一样,如下:

    static {
      System.setProperty("mail.mime.splitlongparameters","false");
    }

    具体原因自行百度了,多得很。

  • 相关阅读:
    nvelocity的Foreach 中使用DataTable数据
    好的博客链接收集
    Uber Shaders
    DOF
    纹理过滤模式中的Bilinear、Trilinear以及Anistropic Filtering
    开通博客了
    代码中的隐式转换问题
    使用C++/CLI封装引用BOOST库的DLL造成的问题
    在Free Radius的PAP认证过程中使用MD5密码
    (转)测试CPU是大端Big endian 还是小端Little Endian的C代码
  • 原文地址:https://www.cnblogs.com/dream-saddle/p/10978113.html
Copyright © 2020-2023  润新知