发送带图片的邮件

背景

开完发票后需要发送邮件信息到客户邮箱中,此时的邮件是带有图片的,为此记录下本次开发过程

实现过程

使用JavaMail

  • 配置文件
<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
<version>1.5.0-b01</version>
</dependency>

​ 如果是SpringBoot项目,已经集成了JavaMailSender,不需要在另外导入。

spring.mail.host=
spring.mail.username=
spring.mail.password=
spring.mail.protocol=smtps
spring.mail.test-connection=false
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.starttls.required=true
# ssl 配置  (非ssl发送一般是25端口,linux服务器一般都是禁用的,所以要切换465)
spring.mail.port=465
spring.mail.default-encoding=UTF-8
spring.mail.properties.mail.smtp.ssl.enable=true
spring.mail.properties.mail.imap.ssl.socketFactory.fallback=false
spring.mail.properties.mail.smtp.ssl.socketFactory.class=com.fintech.modules.base.util.mail.MailSSLSocketFactory

定义你所需要的发票模板

<table style="border-top:none; max-width:760px;border-collapse:collapse;margin: 10px auto;">
    <tbody>
    <tr>
        <td style="background-color: #00a5f9; height: 60px;text-align: left;"> <img
                src="http://www.fapiao.com/static/images/logo.png" width="128" height="45" border="0"
                style="margin-left: 50px; margin-top: 6px;"> </td>
    </tr>
    <tr>
        <td>
            <table style="border-top:none; border-collapse:collapse;width: 760px;margin-top: 15px;table-layout:fixed;"
                   cellspacing="0" cellpadding="0">
                <tbody>
                <tr>
                    <td colspan="2"
                        style="font-weight: bold; text-align: left;font-size: 16px; color: #000;padding-left: 50px;"> 尊敬的用户您好:
                    </td>
                </tr>
                <tr>
                    <td colspan="2"
                        style="text-align: left;font-size: 14px; color: #4d4d4d; font-weight: bold; padding-left: 50px;">
                       ----------已经送达您的发票通账户。 </td>
                </tr>
                <tr>
                    <td>
                        <table cellspacing="0" cellpadding="0"
                               style="border-top:none;border-collapse:collapse;margin-left: 50px;width: 660px;">
                            <tbody>
                            <tr>
                                <td colspan="2"
                                    style="border: 1px solid #00a5f9; color: #00a5f9; height: 40px; line-height: 40px; padding-left: 10px;">
                                    发票抬头:<span th:text="${buyerName}"></span></td>
                            </tr>
                            <tr>
                                <td
                                        style="border-left: 1px solid #00a5f9; color: #00a5f9; height: 40px; line-height: 40px; padding-left: 10px;">
                                    发票代码:<span th:text="${invoiceCode}"></span></td>
                                <td
                                        style="border-left: 1px solid #00a5f9; border-right: 1px solid #00a5f9; color: #00a5f9; height: 40px; line-height: 40px; padding-left: 10px;">
                                    发票号码:<span th:text="${invoiceNo}"></span></td>
                            </tr>
                            <tr>
                                <td
                                        style="border: 1px solid #00a5f9; color: #00a5f9; height: 40px; line-height: 40px; padding-left: 10px; border-right: 0px;">
                                    开票日期:<span th:text="${createtime}"></span></td>
                                <td
                                        style="border: 1px solid #00a5f9; color: #00a5f9; height: 40px; line-height: 40px; padding-left: 10px;">
                                    开票金额:<span th:text="${totalAmount}"></span></td>
                            </tr>
                            </tbody>
                        </table>
                    </td>
                </tr>
                <tr>
                    <td colspan="2" height="30" style="text-align: left;">
                        <p style="font-size: 16px; color: #000; font-weight: bold; margin-left: 50px;">
                            您可以打开微信,扫描下方二维码到发票通公众号内收票。</p>
                    </td>
                </tr>
                <tr>
                    <td colspan="2" style="text-align: center; padding-bottom: 30px;" align="center"> <img
                            src="#weChat"
                            alt="" width="215" height="215"> <span style="display: block; text-align: center;">收票二维码</span> </td>
                </tr>
                <tr>
                    <td colspan="2" height="30" style="font-weight: bold; text-align: left;">
                        <div style="font-size: 16px; color: #000; padding-top: 20px; margin-left: 50px;">您还可以按照以下方式进行收票</div>
                    </td>
                </tr>
                <tr>
                    <td colspan="2" style="text-align: left; padding-left: 50px;word-break : break-all; " align="center">
                        <p style="color: #8e8e8e;">如果点击无效,请复制下方网页地址到浏览器地址栏中打开</p>
                        <p> 发票PDF下载</p> <a
                            th:href="@{${pdfurl}}"
                            target="_blank"
                            style="color: #808080; font-size: 12px;"><span th:text="${pdfurl}"></span></a>
                        </p>
                        <p> 发票图片下载: <a
                                th:href="@{${wechatUrl}}"
                                target="_blank"
                                style="color: #808080; font-size: 12px;"><span th:text="${wechatUrl}"></span></a>
                        </p>
                    </td>
                </tr>
                </tbody>
            </table>
        </td>
    </tr>
    <tr>
        <td
                style="background: #f3f3f3; padding: 0px 10px 10px 10px; line-height: 26px; font-size: 14px; color: #8e8e8e; text-align: left;">
            <table>
                <tbody>
                <tr>
                    <td style="text-align: left; width: 370;">
                        <p
                                style="font-size: 16px; color: #000; margin-left: 50px; line-height: 26px; margin-top: 5px; margin-bottom: 10px; font-weight: bold;">
                            更多收票指引:</p>
                        <p style="margin-left: 50px; color: #8e8e8e; line-height: 26px; margin-top: 5px; margin-bottom: 5px;">
                            <strong>发票通网站www.fapiao.com、发票通APP:</strong> <br>登录后点击“我的发票”查看发票。</p>
                        <p style="margin-left: 50px; color: #8e8e8e; line-height: 26px; margin-top: 5px; margin-bottom: 5px;">
                            <strong>发票通微信公众号:</strong> <br> 关注发票通微信公众号(微信号:fapiaotong) <br>点击 :“个人中心 → 账户绑定 →
                            绑定发票通账户”。今后开电票,微信会有通知提醒您。</p>
                    </td>
                    <td style="padding-left: 80px;">
                        <table style="margin-top: 15px; text-align: center; color: #8e8e8e;">
                            <tbody>
                            <tr>
                                <td style="padding-right: 40px;"> <img src="http://www.fapiao.com/static/images/gzh_100_tmp.jpg"
                                                                       alt="发票通公众号" width="100" height="100"></td>
                                <td style="padding-right: 40px;"> <img src="http://www.fapiao.com/static/images/app.png"
                                                                       alt="发票通APP" width="100" height="100"></td>
                            </tr>
                            <tr>
                                <th style="padding-right: 40px;">发票通公众号</th>
                                <th style="padding-right: 40px;">发票通APP</th>
                            </tr>
                            </tbody>
                        </table>
                    </td>
                </tr>
                </tbody>
            </table>
        </td>
    </tr>
    </tbody>
</table>

将图片转为二维码

public static boolean orCode(String content, String path,Integer width,Integer height) {
        // 图片的格式
        String format = "png";
        // 定义二维码的参数
        HashMap hints = new HashMap();
        // 定义字符集编码格式
        hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
        // 纠错的等级 L > M > Q > H 纠错的能力越高可存储的越少,一般使用M
        hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M);
        // 设置图片边距
        hints.put(EncodeHintType.MARGIN, 2);

        try {
            // 最终生成 参数列表 (1.内容 2.格式 3.宽度 4.高度 5.二维码参数)
            BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, width, height, hints);
            // 写入到本地
            File file = new File(path);
            if (!file.exists()) {
                 file.mkdirs();
            }
            MatrixToImageWriter.writeToPath(bitMatrix, format, file.toPath());
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }

    }

​ 这里是要引入jar包,将图片转化为二维码。如果没需求可直接略过。

    <dependency>
            <groupId>com.google.zxing</groupId>
            <artifactId>core</artifactId>
            <version>3.3.3</version>
        </dependency>
        <dependency>
            <groupId>com.google.zxing</groupId>
            <artifactId>javase</artifactId>
            <version>3.3.3</version>
        </dependency>

发送Service

package com.gddxit.wwis.service;

import com.gddxit.wwis.common.beans.ImageResource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.FileSystemResource;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Component;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;

import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.io.File;
import java.util.Set;


/**
 * @author qiumeng
 * @version 1.0
 * @description
 * @date 2019/4/23 9:07
 */
@Component
public class SendEmailService {


    private static final Logger logger = LoggerFactory.getLogger(SendEmailService.class);
    @Autowired
    private JavaMailSender mailSender;

    @Value("${spring.mail.username}")
    private String from;

    @Autowired
    private TemplateEngine templateEngine;
    /**
     * @description
     * @author qiumeng
     * @date 2019-10-22
     * @param to   目标邮箱
     * @param subject   主体
     * @param content    内容
     */
    public void sendSimpleMail(String[] to, String subject, String content) {
        SimpleMailMessage message = new SimpleMailMessage();
        message.setFrom(from);
        message.setTo(to);
        message.setSubject(subject);
        message.setText(content);
        try{

            mailSender.send(message);
            System.out.println("success");
        }catch (Exception e){
            System.out.println("fail"+e);
        }
    }

    /**
     * @description 发送html邮件
     * @author qiumeng
     * @date 2019-10-22
     * @param to
     * @param subject
     * @param content
     */
    public void sendHtmlMail(String[] to, String subject, String content) {
        MimeMessage message = mailSender.createMimeMessage();
        try {
            MimeMessageHelper helper = new MimeMessageHelper(message, true);   //true表示需要创建一个multipart message
            helper.setFrom(from);
            helper.setTo(to);
            helper.setSubject(subject);
            helper.setText(content, true);
            mailSender.send(message);
            System.out.println("html邮件发送成功");
        } catch (MessagingException e) {
            System.out.println("发送html邮件时发生异常!"+e);
        }
    }

    public void sendAttachmentMail(String[] to, String subject, String content, String filePath) {
        MimeMessage message = mailSender.createMimeMessage();

        try {
            MimeMessageHelper helper = new MimeMessageHelper(message, true);
            helper.setFrom(from);
            helper.setTo(to);
            helper.setSubject(subject);
            helper.setText(content, true);

            FileSystemResource file = new FileSystemResource(new File(filePath));
            String fileName = filePath.substring(filePath.lastIndexOf(File.separator));
            helper.addAttachment(fileName, file);
            //helper.addAttachment("test"+fileName, file);
            mailSender.send(message);
            System.out.println("带附件的邮件已经发送。");
        } catch (MessagingException e) {
            System.out.println("发送带附件的邮件时发生异常!"+e);
        }
    }

    /**
     * 可以用来发送带有图片的HTML模板邮件
     *
     * @param subject:邮件主题
     * @param toWho:收件人
     * @param ccPeoples:抄送人
     * @param bccPeoples:暗送人
     * @param attachments:附件
     * @param templateName:模板名称
     * @param context:模板解析需要的数据
     * @param imageResourceSet:图片资源的资源对象
     */
    public void sendHtmlTemplateMailActual(
            String subject,
            String[] toWho,
            String[] ccPeoples,
            String[] bccPeoples,
            String[] attachments,
            String templateName,
            Context context,
            Set<ImageResource> imageResourceSet){

        //检验参数:邮件主题、收件人、模板名称必须不为空才能够保证基本的逻辑执行
        if(subject == null||toWho == null||toWho.length == 0||templateName == null){

            logger.error("邮件-> {} 无法继续执行,因为缺少基本的参数:邮件主题、收件人、模板名称",subject);

            throw new RuntimeException("模板邮件无法继续发送,因为缺少必要的参数!");
        }

        //日志这个邮件的基本信息
        logger.info("发送HTML模板邮件:主题->{},收件人->{},抄送人->{},密送人->{},附件->{},模板名称->{},模板解析参数->{},图片资源->{})",subject,toWho,ccPeoples,bccPeoples,attachments,templateName,context,imageResourceSet);

        try{

            //context不能够为空,需要进行检查
            if(context == null){

                context = new Context();
                logger.info("邮件->{} 的context为空!",subject);
            }

            //模板引擎处理模板获取到HTML字符串,这里会可能会抛出一个继承于RuntimeException的模板引擎异常
            String content = templateEngine.process(templateName,context);

            MimeMessage mimeMessage = mailSender.createMimeMessage();

            //默认编码为UTF-8
            MimeMessageHelper helper = new MimeMessageHelper(mimeMessage,true,"UTF-8");

            //处理内联的图片资源的占位转换
            content = handleInLineImageResourceContent(helper,subject,content,imageResourceSet);

            logger.info("解析邮件结果->{}",content);

            //处理基本信息
            boolean continueProcess = handleBasicInfo(helper,subject,content,toWho,ccPeoples,bccPeoples,true);

            if(!continueProcess){

                logger.error("邮件基本信息出错:主题->{}",subject);

                return;
            }

            //内联资源的资源附加,这个必须要放置在设置基本信息的操作后面,或者是全部内容解析完毕后才可以,不能边解析,边占位
            handleInLineImageResource(helper,subject,imageResourceSet);

            //处理附件
            handleAttachment(helper,subject,attachments);

            //发送该邮件
            mailSender.send(mimeMessage);

            logger.info("发送邮件成功:主题->{}",subject);

        }catch(MessagingException e){

            e.printStackTrace();

            logger.error("发送邮件失败:邮件主题->{}",subject);
        }
    }

    /**
     * 处理内嵌图片的模板HTML邮件,返回一个已经修改过后的HTML字符串
     *
     * @param mimeMessageHelper:邮件信息包装类
     * @param subject:邮件主题
     * @param originContent:模板引擎所解析出来的原始HTML邮件
     * @param imageResourceSet:图片资源集合,用于字符集站位填充
     *
     * @return :返回处理后的邮件内容
     */
    private String handleInLineImageResourceContent(MimeMessageHelper mimeMessageHelper,String subject,String originContent,Set<ImageResource> imageResourceSet){

        //处理内嵌的HTML图片文件
        if(imageResourceSet != null&&imageResourceSet.size() > 0){

            //资源的占位符ID
            String rscId;
            //资源的路径
            String resourcePath = null;
            //图片的位置信息
            String placeHolder;
            //图片资源文件
            FileSystemResource resource;

            for(ImageResource imageResource : imageResourceSet){

                //获取图片资源的基本信息
                rscId = imageResource.getId();
                placeHolder = imageResource.getPlaceholder();
                resourcePath = imageResource.getImageFilePath();

                resource = new FileSystemResource(new File(resourcePath));

                //判断图片资源是否存在
                if(!resource.exists()){

                    logger.warn("邮件->{} 内联图片->{} 找不到",subject,resourcePath);

                    continue;
                }

                //替换图片资源在HTML中的位置
                originContent = originContent.replace("\"" + ImageResource.PLACEHOLDERPREFIX + placeHolder + "\"","\'cid:" + rscId + "\'");
            }
        }
        return originContent;
    }

    /**
     * 填充文本数据,因为数据填充必须在设置基本数据后面进行,所以讲内容和数据的填充进行分离
     *
     * @param mimeMessageHelper
     * @param subject:邮件主题,用于日志记录
     * @param imageResourceSet:资源
     */
    private void handleInLineImageResource(MimeMessageHelper mimeMessageHelper,String subject,Set<ImageResource> imageResourceSet){

        if(imageResourceSet != null&&imageResourceSet.size() > 0){

            FileSystemResource resource;

            for(ImageResource imageResource : imageResourceSet){

                resource = new FileSystemResource(new File(imageResource.getImageFilePath()));

                if(!resource.exists()){

                    logger.warn("邮件->{} 的内联图片文件->{} 不存在!",subject,imageResource);

                    continue;
                }

                try{

                    //添加内联资源
                    mimeMessageHelper.addInline(imageResource.getId(),resource);

                }catch(MessagingException e){
                    e.printStackTrace();

                    logger.error("邮件->{} 的内联图片文件->{} 添加错误!",subject,imageResource);
                }
            }
        }
    }


    /**
     * 用于处理附件信息,附件需要 MimeMessage 对象
     *
     * @param mimeMessageHelper:处理附件的信息对象
     * @param subject:邮件的主题,用于日志记录
     * @param attachmentFilePaths:附件文件的路径,该路径要求可以定位到本机的一个资源
     */
    private void handleAttachment(MimeMessageHelper mimeMessageHelper,String subject,String[] attachmentFilePaths){

        //判断是否需要处理邮件的附件
        if(attachmentFilePaths != null&&attachmentFilePaths.length > 0){

            FileSystemResource resource;

            String fileName;

            //循环处理邮件的附件
            for(String attachmentFilePath : attachmentFilePaths){

                //获取该路径所对应的文件资源对象
                resource = new FileSystemResource(new File(attachmentFilePath));

                //判断该资源是否存在,当不存在时仅仅会打印一条警告日志,不会中断处理程序。
                // 也就是说在附件出现异常的情况下,邮件是可以正常发送的,所以请确定你发送的邮件附件在本机存在
                if(!resource.exists()){

                    logger.warn("邮件->{} 的附件->{} 不存在!",subject,attachmentFilePath);

                    //开启下一个资源的处理
                    continue;
                }

                //获取资源的名称
                fileName = resource.getFilename();

                try{

                    //添加附件
                    mimeMessageHelper.addAttachment(fileName,resource);

                }catch(MessagingException e){

                    e.printStackTrace();

                    logger.error("邮件->{} 添加附件->{} 出现异常->{}",subject,attachmentFilePath,e.getMessage());
                }
            }
        }
    }

    /**
     * 处理二进制邮件的基本信息,比如需要带附件的文本邮件、HTML文件、图片邮件、模板邮件等等
     *
     * @param mimeMessageHelper:二进制文件的包装类
     * @param subject:邮件主题
     * @param content:邮件内容
     * @param toWho:收件人
     * @param ccPeoples:抄送人
     * @param bccPeoples:暗送人
     * @param isHtml:是否是HTML文件,用于区分带附件的简单文本邮件和真正的HTML文件
     *
     * @return :返回这个过程中是否出现异常,当出现异常时会取消邮件的发送
     */
    private boolean handleBasicInfo(MimeMessageHelper mimeMessageHelper,String subject,String content,String[] toWho,String[] ccPeoples,String[] bccPeoples,boolean isHtml){

        try{
            //设置必要的邮件元素

            //设置发件人
            mimeMessageHelper.setFrom(from);
            //设置邮件的主题
            mimeMessageHelper.setSubject(subject);
            //设置邮件的内容,区别是否是HTML邮件
            mimeMessageHelper.setText(content,isHtml);
            //设置邮件的收件人
            mimeMessageHelper.setTo(toWho);

            //设置非必要的邮件元素,在使用helper进行封装时,这些数据都不能够为空

            if(ccPeoples != null)
                //设置邮件的抄送人:MimeMessageHelper # Assert.notNull(cc, "Cc address array must not be null");
                mimeMessageHelper.setCc(ccPeoples);

            if(bccPeoples != null)
                //设置邮件的密送人:MimeMessageHelper # Assert.notNull(bcc, "Bcc address array must not be null");
                mimeMessageHelper.setBcc(bccPeoples);

            return true;
        }catch(MessagingException e){
            e.printStackTrace();

            logger.error("邮件基本信息出错->{}",subject);
        }


        return false;
    }
}


调用

  • 模板中定义的#{uri},只需在代码中直接复制就好,但是图片资源需要另外处理
   Context context = new Context();
   context.setVariable("buyerName", buyerName);
   context.setVariable("invoiceCode", invoiceCode);
   context.setVariable("invoiceNo", invoiceNo);
   context.setVariable("createtime", createtime);
   context.setVariable("totalAmount", totalAmount);
   context.setVariable("pdfurl", pdfurl);
   context.setVariable("wechatUrl", wechatUrl);


   //设置内联的图片资源 这里的weChat对应模板的#{weChat}
   ImageResource imageResource = new ImageResource("weChat", path);

    Set<ImageResource> imageResources = new HashSet<>();

    imageResources.add(imageResource);

    ## invoice 是上面定义的thymeleaf模板
    sendEmailService.sendHtmlTemplateMailActual
                    ("邮件主题	",
                            new String[]{buyerEmail},
                            null,
                            null,
                            null,
                            "invoice",
                            context,
                            imageResources);

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值