美文网首页
JavaMail的使用详解,看完真的明白了

JavaMail的使用详解,看完真的明白了

作者: 会转圈儿的冷冷凉 | 来源:发表于2020-04-22 16:56 被阅读0次
    总体介绍:

    公司近期接到需求,由于安卓和IOS客户端在转发邮件时会重新下载一遍老邮件里附件,这个过程比较耗时,体验不佳,现在需要实现客户端走服务器来转发,免去下载的过程
    我们先了解一下两种协议格式:

    SMTP:邮件发送协议 ssl对应端口465 非ssl对应端口25
    IMAP:收邮件协议 ssl对应端口993 非ssl对应端口143

    以上描述的是邮箱服务器的默认端口 可自定义或变更 但主流端口就是以上这么几个

    附上JavaMail官网地址,遇到问题时在里面会发现很多灵感:
    https://javaee.github.io/javamail/docs/api/

    maven引用
    <dependency>
                <groupId>com.sun.mail</groupId>
                <artifactId>javax.mail</artifactId>
                <version>1.6.2</version>
            </dependency>
    

    JavaMail邮件格式介绍

    如果想了解邮件如何发送,我们必须了解邮件消息的正文结构
    我们先了解一下发邮件时的流程:

    //创建消息对象
    MimeMessage message = new MimeMessage(session);
    //创建消息正文
    MimeMultipart mimmultiPart = new MimeMultipart();
    //创建消息体
    MimeBodyPart mimeBodyPart = new MimeBodyPart();
    //将消息体包裹在正文中
    mimmultiPart.addBodyPart(mimeBodyPart);
    //将正文包裹在消息对象中
    message.setContent(mimmultiPart )
    //发邮件协议是smtp  收邮件协议是imap
    Transport transport = session.getTransport("smtp");
    //发送邮件
    transport.sendMessage(mimeMessage,"某收件人");
    

    MimeMessage 包含了 MimeMultipart,
    MimeMultipart 包含了 MimeBodyPart
    这里面比较绝妙的就是
    MimeBodyPart 其实反过来也可以包含MimeMultipart 后面我们将看到
    以下4种格式均为笔者通过FoxMail发送邮件,并通过JavaMail去找邮件而来的结果

    ①纯文本

    邮件正文仅带有文本文字

    --- MimeMutiPart("ALTERNATIVE")
              MimeBodyPart0("TEXT/PLAIN")
              MimeBodyPart1("TEXT/HTML")
    

    使用FoxMail发邮件时,尽管是最简单的文本,也是一个MimeMutiPart包含了两个MimeBodyPart,一个对应纯文本,另一个对应html

    ②纯文本带附件

    邮件正文包含文本文字+附件

    --- MimeMutiPart("MIXED")
    |------MimeBodyPart0 ---- MimeMutiPart("ALTERNATIVE")
    |            MimeBodyPart0("TEXT/PLAIN")
    |            MimeBodyPart1("TEXT/HTML")
    |------MimeBodyPart1 (""APPLICATION/OCTET-STREAM")
    

    文本的内容仍然是一个MutiPart包含了两个BodyPart 只不过此部分被包含进了MimeBodyPart0
    附件的部分是一个独立的MimeBodyPart1

    ③富文本

    邮件正文包含富文本文字

    --- MimeMutiPart("RELATED")
    |------MimeBodyPart0 ---- MimeMutiPart("ALTERNATIVE")
    |            MimeBodyPart0("TEXT/PLAIN")
    |            MimeBodyPart1("TEXT/HTML")
    |------MimeBodyPart1("IMAGE/GIF")
    |------MimeBodyPart2("IMAGE/GIF")
    |------MimeBodyPart3("IMAGE/GIF")
    

    文本的内容还是相同的,一个MutiPart包含了两个BodyPart 此MutiPart被包含进了
    MimeBodyPart0
    MimeBodyPart1、MimeBodyPart2、MimeBodyPart3属于"INLINE"的附件,而非attach的附件属于富文本中的图片和表情等资源

    ④富文本带附件

    邮件正文包含富文本文字+附件

    --- MimeMutiPart("MIXED")
    |------MimeBodyPart0 ---- MimeMutiPart("RELATED")
    |            MimeBodyPart0 ---- MimeMutiPart("ALTERNATIVE")
    |                 MimeBodyPart0("TEXT/PLAIN")
    |                 MimeBodyPart1("TEXT/HTML")
    |            MimeBodyPart1("IMAGE/GIF")
    |            MimeBodyPart2("IMAGE/GIF")
    |            MimeBodyPart3("IMAGE/GIF")
    |------MimeBodyPart1 ("APPLICATION/OCTET-STREAM")
    |------MimeBodyPart2 ("APPLICATION/OCTET-STREAM")
    |------MimeBodyPart3 ("APPLICATION/OCTET-STREAM")
    

    这是最复杂的一种,也是可以基本满足我们日常需求的一种,最里面的一层还是ALTERNATIVE的MutiPart包含了两个文本类型,同层级包含着"INLINE"的附件
    最外层是一些真正的附件

    以上四种类型基本上涵盖了我们日常使用的格式,不难发现MimeMutiPart一共有三种类型:
    1.MIXED:包含附件的文档
    2.ALTERNATIVE:

    包含text文档和html文档,为什么是两种 一般邮箱客户端比如FoxMail 可以切换阅读模式
    一个是超文本阅读格式 对应html
    一个是纯文本(去掉文中图片和表情)格式 对应text

    3.RELATED:图文(富文本)
    发送邮件代码样例

    首先,邮箱服务都是基于Session会话开始,首先我们构建一个Session

    public final static String SSL_FACTORY = "javax.net.ssl.SSLSocketFactory";
    public final static String DEFAULT_FACTORY = "javax.net.DefaultSocketFactory";
    public static Session generSession(Dto_MainForward dto_mainForward,MailServers mailinfo) throws NoSuchProviderException, GeneralSecurityException {
            Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
            MailSSLSocketFactory socketFactory= new MailSSLSocketFactory();
            socketFactory.setTrustAllHosts(true);
            Properties props = System.getProperties();
            props.put("mail.imaps.ssl.socketFactory", socketFactory);
            //此处规定连接邮箱的方式是https还是http 默认一种即可
            props.setProperty("mail.imap.socketFactory.class", mailinfo.getSslable() ? SSL_FACTORY : DEFAULT_FACTORY);
            //对应邮箱服务器的端口 ssl对应465 普通对应25 视实际情况而定
            props.setProperty("mail.imap.socketFactory.port", mailinfo.getMailboxPort().toString());
            props.setProperty("mail.store.protocol", "imap");
            //收信时邮箱服务器地址 比如qq邮箱对应imap.exmail.qq.com
            props.setProperty("mail.imap.host", mailinfo.getMailbox());
            //收信使用的端口  ssl对应993 普通对应143 视实际情况而定
            props.setProperty("mail.imap.port", mailinfo.getMailboxPort().toString());
            props.setProperty("mail.imap.auth.login.disable", "true");
            props.setProperty("mail.imap.partialfetch", "false");
            props.setProperty("mail.imaps.partialfetch", "false");
            Session session = Session.getInstance(props, null);
            //是否开启debug模式 如果设置成true 你将得到大量邮件log信息帮助调试
            session.setDebug(false);
            return session;
        }
    
    
    构建纯文本无附件的邮件:
    public void SendJustText(Session session) throws MessagingException {
            MimeMessage message = new MimeMessage(session);
            //邮件标题
            message.setSubject("纯文本");
            //邮件发送人
            message.setFrom(new InternetAddress(MailService.senderMail));
            //邮件接收人     message.setRecipients(Message.RecipientType.TO,InternetAddress.parse(MailService.senderMail));
            //构建最外层的MimeMutipart
            MimeMultipart mimeMultipart = new MimeMultipart("alternative");
            //构建邮件正文Body
            MimeBodyPart mimeBodyPart = new MimeBodyPart();
            mimeBodyPart.setContent("我是纯文本正文","TEXT/PLAIN; charset=GB2312");
            //将body追加到Mutipart
            mimeMultipart.addBodyPart(mimeBodyPart);
            message.setContent(mimeMultipart);
            //连接邮箱服务器
            Transport transport = session.getTransport("smtp");
            //可以看成自己的邮箱登录 用时自行替换参数
            transport.connect(MailService.mailHost, MailService.senderMail, MailService.password);
            //发送消息
            transport.sendMessage(message, message.getAllRecipients());
        }
    

    这样我们就得到了一个纯文本的邮件


    纯文本.png
    构建纯文本带附件的邮件:
    --- MimeMutiPart("MIXED")
    |------MimeBodyPart0 ---- MimeMutiPart("ALTERNATIVE")
    |            MimeBodyPart0("TEXT/PLAIN")
    |            MimeBodyPart1("TEXT/HTML")
    |------MimeBodyPart1 (""APPLICATION/OCTET-STREAM")
    |------MimeBodyPart2 (""APPLICATION/OCTET-STREAM")
    
    public void SendAttachText(Session session) throws MessagingException, IOException {
            MimeMessage message = new MimeMessage(session);
            //邮件标题
            message.setSubject("纯文本带附件");
            //邮件发送人
            message.setFrom(new InternetAddress(MailService.senderMail));
            //邮件接收人
            message.setRecipients(Message.RecipientType.TO,InternetAddress.parse(MailService.senderMail));
    
            //构建最外层的MimeMutipart
            MimeMultipart mimeMultipart = new MimeMultipart("mixed");
            //构建邮件正文Body
            MimeBodyPart mimeBodyPart = new MimeBodyPart();
            mimeBodyPart.setContent("我是纯文本带附件正文","TEXT/PLAIN; charset=GB2312");
            //将body追加到Mutipart
            mimeMultipart.addBodyPart(mimeBodyPart);
            //添加第一个附件
            mimeMultipart.addBodyPart(getAttchment1());
            //添加第二个附件
            mimeMultipart.addBodyPart(getAttchment2());
            message.setContent(mimeMultipart);
            //连接邮箱服务器
            Transport transport = session.getTransport("smtp");
            //可以看成自己的邮箱登录
            transport.connect(MailService.mailHost, MailService.senderMail, MailService.password);
            //发送消息
            transport.sendMessage(message, message.getAllRecipients());
        }
    
    
        /**
         * 构建附件1
         * @return
         * @throws IOException
         * @throws MessagingException
         */
        private MimeBodyPart getAttchment1() throws IOException, MessagingException {
            MimeBodyPart attachPart = new MimeBodyPart();
            File file = new File("C:\\Users\\xxx\\Desktop\\spring初始化.txt");
            attachPart.attachFile(file);
            //解决中文乱码问题
            attachPart.setFileName(MimeUtility.encodeText(file.getName()));
            return attachPart;
        }
        /**
         * 构建附件2
         * @return
         * @throws IOException
         * @throws MessagingException
         */
        private MimeBodyPart getAttchment2() throws IOException, MessagingException {
            MimeBodyPart attachPart = new MimeBodyPart();
            File file = new File("C:\\Users\\xxx\\Desktop\\长春农商行问题修复\\操作说明.txt");
            attachPart.attachFile(file);
            //解决中文乱码问题
            attachPart.setFileName(MimeUtility.encodeText(file.getName()));
            return attachPart;
        }
    

    这样我们就得到了一个纯文本带附件的邮件


    纯文本带附件.png

    这里我们会发现一个问题:

    一般来讲邮件附件不是从本地获取 而是服务器接收客户端发过来的文件,这样如何写呢?于是我们的getAttchment1方法就变成了这样:
        /**
         * 构建附件1
         * @return
         * @throws IOException
         * @throws MessagingException
         */
       private MimeBodyPart getAttchment1(MultipartFile attachFile) throws IOException, MessagingException {
            if (attachFile != null) {
                MimeBodyPart attachPart = new MimeBodyPart();
                ByteArrayDataSource byteArrayDataSource = new ByteArrayDataSource(multipartFile.getInputStream(),multipartFile.getContentType());
                    //为文件流设置文件名称
                    byteArrayDataSource.setName(multipartFile.getOriginalFilename());
                    DataHandler dataHandler = new DataHandler(new ByteArrayDataSource(multipartFile.getInputStream(),multipartFile.getContentType()));
                attachPart.setDataHandler(new DataHandler(dataSource));
                //这里文件源名称由客户端上传 一般都是经过url编码的 我们先解码 再转成邮箱的编码    
                attachPart.setFileName(MimeUtility.encodeText(URLDecoder.decode(attachFile.getOriginalFilename(), "utf-8")));
            }
        }
    
    构建图文混合无附件的邮件:
    --- MimeMutiPart("RELATED")
    |------MimeBodyPart0 ---- MimeMutiPart("ALTERNATIVE")
    |            MimeBodyPart0("TEXT/PLAIN")
    |            MimeBodyPart1("TEXT/HTML")
    |------MimeBodyPart1("IMAGE/GIF")
    |------MimeBodyPart2("IMAGE/GIF")
    |------MimeBodyPart3("IMAGE/GIF")
    
    public void SendJianShuPicText(Session session) throws MessagingException {
            MimeMessage message = new MimeMessage(session);
            //邮件标题
            message.setSubject("富文本无附件");
            //邮件发送人
            message.setFrom(new InternetAddress(MailService.senderMail));
            //邮件接收人
            message.setRecipients(Message.RecipientType.TO,InternetAddress.parse(MailService.senderMail));
            //构建最外层的MimeMutipart
            MimeMultipart mimeMultipart = new MimeMultipart("related");
            //将文本的bodyPart追加到Mutipart
            mimeMultipart.addBodyPart(getAlterBodyPart());
            message.setContent(mimeMultipart);
            //连接邮箱服务器
            Transport transport = session.getTransport("smtp");
            //可以看成自己的邮箱登录
            transport.connect(MailService.mailHost, MailService.senderMail, MailService.password);
            //发送消息
            transport.sendMessage(message, message.getAllRecipients());
        }
    
        public MimeBodyPart getAlterBodyPart() throws MessagingException {
            //要返回的alterBodyPart
            MimeBodyPart alterBodyPart = new MimeBodyPart();
            //定义alterBodyPart下的mutiPart
            MimeMultipart alterMutiPart = new MimeMultipart("ALTERNATIVE");
            //定义文本bodyPart
            MimeBodyPart textBodyPart = new MimeBodyPart();
            textBodyPart.setContent("我是富文本无附件正文","TEXT/PLAIN; charset=GB2312");
            //定义html的bodyPart
            MimeBodyPart htmlBodyPart =new MimeBodyPart();
            htmlBodyPart.setContent("<html><div><h3>我是富文本无附件正文</h3></div></br><img src='cid:lenglengliang1'/></br><strong>我的冷冷凉2</strong></br><img src='cid:lenglengliang2'/></br>不下地的冷冷凉</html>","TEXT/HTML; charset=GB2312");
            //添加文本的bodyPart
            alterMutiPart.addBodyPart(textBodyPart);
            //添加html的bodyPart
            alterMutiPart.addBodyPart(htmlBodyPart);
            //定义inline附件的bodyPart
            MimeBodyPart inlineBodyPart = new MimeBodyPart();
            DataSource dataSource = new FileDataSource(new File("D:\\一些图片\\无聊的图片\\未来战士\\timg.jpg"));
            inlineBodyPart.setDataHandler(new DataHandler(dataSource));
            inlineBodyPart.setDisposition(MimeBodyPart.INLINE);
            inlineBodyPart.setContentID("<lenglengliang1>");
            //添加inline的bodyPart
            alterMutiPart.addBodyPart(inlineBodyPart);
    
            MimeBodyPart inlineBodyPart2 = new MimeBodyPart();
            DataSource dataSource2 = new FileDataSource(new File("D:\\一些图片\\无聊的图片\\未来战士\\timg (2).jpg"));
            inlineBodyPart2.setDataHandler(new DataHandler(dataSource2));
            inlineBodyPart2.setDisposition(MimeBodyPart.INLINE);
            inlineBodyPart2.setContentID("<lenglengliang2>");
            //添加inline的bodyPart
            alterMutiPart.addBodyPart(inlineBodyPart2);
            //装载mutiPart
            alterBodyPart.setContent(alterMutiPart);
            return alterBodyPart;
        }
    

    这样我们就得到了一个富文本无附件的邮件


    富文本无附件

    将图(不管是图片还是一些表情)添加到正文的逻辑是:
    1.首先保证html文本中包含img标签 富文本在邮箱中显示的其实就是一个html页面
    2.图片和<img src="" />的关联关系就是以下代码创建的

     htmlBodyPart.setContent("<html><img src='cid:lenglengliang1'/></html>","TEXT/HTML; charset=GB2312");
     inlineBodyPart.setContentID("<lenglengliang1>");
    
    html中的src 必须包含cid: 这是rfc标准格式
    bodyPart.setContentID 并不包含"cid:",但却有 "<>" 标签 这是邮件的标准格式,如果不带,发送时可能没问题,但是这个邮件被安卓或者ios端转发解析时可能出现问题,他们的标准库判断了"<>"
    PS:项目中笔者曾经为图文中的图发送后变成了附件而苦恼了很久,原因就是这个cid src中需要带但setContentID时却不需要带
    构建图文混合带附件的邮件:

    这是最复杂的也是最常用的

    --- MimeMutiPart("MIXED")
    |------MimeBodyPart0 ---- MimeMutiPart("RELATED")
    |            MimeBodyPart0 ---- MimeMutiPart("ALTERNATIVE")
    |                 MimeBodyPart0("TEXT/PLAIN")
    |                 MimeBodyPart1("TEXT/HTML")
    |            MimeBodyPart1("IMAGE/GIF")
    |            MimeBodyPart2("IMAGE/GIF")
    |            MimeBodyPart3("IMAGE/GIF")
    |------MimeBodyPart1 ("APPLICATION/OCTET-STREAM")
    |------MimeBodyPart2 ("APPLICATION/OCTET-STREAM")
    |------MimeBodyPart3 ("APPLICATION/OCTET-STREAM")
    
    public void SendJianShuAttachPicText(Session session) throws MessagingException, IOException {
            MimeMessage message = new MimeMessage(session);
            //邮件标题
            message.setSubject("富文本带附件");
            //邮件发送人
            message.setFrom(new InternetAddress(MailService.senderMail));
            //邮件接收人
            message.setRecipients(Message.RecipientType.TO,InternetAddress.parse(MailService.senderMail));
            //构建最外层的MimeMutipart
            MimeMultipart mimeMultipart = new MimeMultipart("related");
            //将文本的bodyPart追加到Mutipart
            mimeMultipart.addBodyPart(getAlterBodyPart());
            message.setContent(mimeMultipart);
            //连接邮箱服务器
            Transport transport = session.getTransport("smtp");
            //可以看成自己的邮箱登录
            transport.connect(MailService.mailHost, MailService.senderMail, MailService.password);
            //发送消息
            transport.sendMessage(message, message.getAllRecipients());
        }
    
        public MimeBodyPart getAlterBodyPart() throws MessagingException, IOException {
            //要返回的alterBodyPart
            MimeBodyPart alterBodyPart = new MimeBodyPart();
            //定义alterBodyPart下的mutiPart
            MimeMultipart alterMutiPart = new MimeMultipart("ALTERNATIVE");
            //定义文本bodyPart
            MimeBodyPart textBodyPart = new MimeBodyPart();
            textBodyPart.setContent("我是富文本带附件正文","TEXT/PLAIN; charset=GB2312");
            //定义html的bodyPart
            MimeBodyPart htmlBodyPart =new MimeBodyPart();
            htmlBodyPart.setContent("<html><div><h3>我是富文本带附件正文</h3></div></br><img src='cid:lenglengliang1'/></br><strong>我的冷冷凉2</strong></br><img src='cid:lenglengliang2'/></br>不下地的冷冷凉</html>","TEXT/HTML; charset=GB2312");
            //添加文本的bodyPart
            alterMutiPart.addBodyPart(textBodyPart);
            //添加html的bodyPart
            alterMutiPart.addBodyPart(htmlBodyPart);
            //定义inline附件的bodyPart
            MimeBodyPart inlineBodyPart = new MimeBodyPart();
            DataSource dataSource = new FileDataSource(new File("D:\\一些图片\\无聊的图片\\未来战士\\timg.jpg"));
            inlineBodyPart.setDataHandler(new DataHandler(dataSource));
            inlineBodyPart.setDisposition(MimeBodyPart.INLINE);
            inlineBodyPart.setContentID("<lenglengliang1>");
            //添加inline的bodyPart
            alterMutiPart.addBodyPart(inlineBodyPart);
    
            MimeBodyPart inlineBodyPart2 = new MimeBodyPart();
            DataSource dataSource2 = new FileDataSource(new File("D:\\一些图片\\无聊的图片\\未来战士\\timg (2).jpg"));
            inlineBodyPart2.setDataHandler(new DataHandler(dataSource2));
            inlineBodyPart2.setDisposition(MimeBodyPart.INLINE);
            inlineBodyPart2.setContentID("<lenglengliang2>");
            //添加inline的bodyPart
            alterMutiPart.addBodyPart(inlineBodyPart2);
            //添加第一个附件
            alterMutiPart.addBodyPart(getAttchment1());
            //添加第二个附件
            alterMutiPart.addBodyPart(getAttchment2());
            //添加第三个附件
            alterMutiPart.addBodyPart(getAttchment3());
            //装载mutiPart
            alterBodyPart.setContent(alterMutiPart);
            return alterBodyPart;
        }
    
        /**
         * 构建附件1
         * @return
         * @throws IOException
         * @throws MessagingException
         */
        private MimeBodyPart getAttchment1() throws IOException, MessagingException {
            MimeBodyPart attachPart = new MimeBodyPart();
            File file = new File("C:\\Users\\yangzhi\\Desktop\\spring初始化.txt");
            attachPart.attachFile(file);
            attachPart.setFileName(MimeUtility.encodeText(file.getName()));
            attachPart.setDisposition(MimeBodyPart.ATTACHMENT);
            return attachPart;
        }
    
        /**
         * 构建附件2
         * @return
         * @throws IOException
         * @throws MessagingException
         */
        private MimeBodyPart getAttchment2() throws IOException, MessagingException {
            MimeBodyPart attachPart = new MimeBodyPart();
            File file = new File("C:\\Users\\yangzhi\\Desktop\\转发富文本(ceomg).eml");
            attachPart.attachFile(file);
            attachPart.setFileName(MimeUtility.encodeText(file.getName()));
            attachPart.setDisposition(MimeBodyPart.ATTACHMENT);
            return attachPart;
        }
        /**
         * 构建附件3
         * @return
         * @throws IOException
         * @throws MessagingException
         */
        private MimeBodyPart getAttchment3() throws IOException, MessagingException {
            MimeBodyPart attachPart = new MimeBodyPart();
            File file = new File("C:\\Users\\yangzhi\\Desktop\\邮箱开发准备\\邮箱id比对.txt");
            attachPart.attachFile(file);
            attachPart.setFileName(MimeUtility.encodeText(file.getName()));
            attachPart.setDisposition(MimeBodyPart.ATTACHMENT);
            return attachPart;
        }
    

    这样我们就得到了一个富文本带附件的邮件


    富文本带附件.png

    邮件的结构精美的地方就是MutiPart和BodyPart的相互转换,文本虽多,但很多点都可以封装,为了方便看笔者就直接上的流水账,邮件的4个基本结构是构建我们代码思路的前提,感谢观看,如果此文对您有帮助 请点个小红心支持一下,后续的收邮件以及邮件的回执、一些坑会单独拿出来分享

    相关文章

      网友评论

          本文标题:JavaMail的使用详解,看完真的明白了

          本文链接:https://www.haomeiwen.com/subject/vzzevhtx.html