邮箱,短信等消息站台服务总结

        最近公司准备搞分布式,需要将原代码按功能等一个个抽离出来,我负责弄邮箱短信部分,经历了半个月的折磨,总算搞出来了,这里做个总结,有许多之前没弄过的,算是由0到1的一个过程。

        整个思路过程如下:

         比较简单的,对外暴露的仅仅只是api接口,内部最重要的是一个实体类,用于存储需上传的数据,再发送给mq,然后消费mq中的信息。

         这其中有几个问题需要总结的,算是这个过程中卡得我难受的点。

        第一:设计上,对外暴露的api接口只有存储对象和mq发送方法,这里是一个小技巧,逻辑处理封装起来,并不对外提供服务。存储对象类设计上还是有点意思的,用到了内部类,这里记录下来,以后做参考:

package cn.coralglobal.message.api.service;

import cn.coralglobal.message.api.exception.MessageCenterBuilderException;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.io.Serializable;

/**
 * @Description: 消息主体对象
 * @Author chenjianwen
 * @Date 2020-03-25
 **/
@Data
@Slf4j
public class MessageSubject implements Serializable {

    private static final long serialVersionUID = 4901512199571414865L;

    /**
     * 发送时间
     */
    private long timestamp = System.currentTimeMillis();

    /**
     * 模版名称
     */
    private String templateName;

    /**
     * 模版code
     */
    private String templateCode;

    /**
     * 替换文本,和模版中的占位符严格一一对应
     */
    private String[] replace;

    /**
     * 消息受众(可以是userId或者用户手机号或邮箱)
     */
    private String[] users;

    /**
     * 发送消息的站台(coralglobal或crm或其他)
     */
    private String platform;

    public MessageSubject(){}

    public MessageSubject(Builder builder){
        setTimestamp(builder.timestamp);
        setTemplateName(builder.templateName);
        setTemplateCode(builder.templateCode);
        setReplace(builder.replace);
        setUsers(builder.users);
        setPlatform(builder.platform);
    }

    public static Builder newBuilder(){
        return new Builder();
    }

    public static Builder newBuilder(MessageSubject ms){
        Builder b = new Builder();
        b.timestamp = ms.getTimestamp();
        b.templateName = ms.getTemplateName();
        b.templateCode = ms.getTemplateCode();
        b.replace = ms.getReplace();
        b.users = ms.getUsers();
        b.platform = ms.getPlatform();
        return b;
    }

    public static final class Builder{
        private long timestamp;
        private String templateName;
        private String templateCode;
        private String[] replace;
        private String[] users;
        private String platform;

        private Builder(){}

        public Builder timestamp(long timestamp){
            this.timestamp = timestamp;
            return this;
        }

        public Builder name(String templateName){
            this.templateName = templateName;
            return this;
        }

        public Builder template(String code) throws MessageCenterBuilderException {
            if(code == null || "".equals(code)){
                log.error("message-center-api|MessageSubject|template(String code)|模版编码不能为空");
                throw new MessageCenterBuilderException("message-center|MessageSubject|template(String code)|模版编码不能为空");
            }
            this.templateCode = code;
            return this;
        }

        public Builder replace(String... replace){
            this.replace = replace;
            return this;
        }

        public Builder users(String... users){
            this.users = users;
            return this;
        }

        public Builder platform(String platform){
            this.platform = platform;
            return this;
        }

        public MessageSubject build() throws MessageCenterBuilderException {
            if(this.templateCode == null || "".equals(this.templateCode)){
                log.error("message-center-api|MessageSubject|build()|templateCode不能为空");
                throw new MessageCenterBuilderException("message-center-api|MessageSubject|build()|templateCode不能为空");
            }
            return new MessageSubject(this);
        }
    }
}

    第二:用mq发送数据的时候,先将数据序列化成字节数据会好点,用下面这个方法:

com.fasterxml.jackson.databind.ObjectMapper;

 其maven依赖是:

<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.10.3</version>
</dependency>

然后监听器那边转换一下即可:

        第三: 由于它是一个单独的服务,因此最终需要打成jar到我们公司本地的私服并生成maven依赖,以供其他的项目使用。所以需要分两步完成:

        (1)我需要现在本地repository仓库生成jar,那问题来了,如何在本地生成jar,这里涉及到了maven的三个命令:

          mvn clean package    在项目目录下的target目录下生成jar
          mvn clean install         打包jar到target目录下,且打包到本地repository
          mvn clean deploy       打包jar到target目录下,且打包到本地repository,且打包到远程repository

          这里在本地仓库测试,因此使用mvn clean install,或者直接在idea上的maven可视化操作,如下:

           

         (2)在公司私服生成jar有点麻烦,花了很多时间才搞定,首先,需要在settings.xml中<servers></servers>标签对中添加本地私服的服务器用户名和密码,如下:

然后在项目中的pom.xml文件中添加如下标签对:

然后使用mvn clean deploy命令就能将服务打成jar包上传到本地私服。

      (2)如何引用本地私服的maven依赖,除了在pom.xml中引用<dependency></dependency>依赖之外,还需要引用本地私服仓库的地址,如下:

这样,就能引用本地私服仓库的maven的jar包了。

=========================================================================================

后面有个需求,要求邮箱可以发送文件,由于我邮箱是走rabbitmq的,所以刚开始总是出问题:

我先是尝试走controller用postman调试的,如下:

 MultipartFile文件类引用的是spring的:

import org.springframework.web.multipart.MultipartFile;

由于我这里是走mq的,如果直接将文件放进去,序列化成字节数组会报如下错误:

No serializer found for class java.io.FileDescriptor and no properties disconnected

所以,我需要先把文件转换成字符串,这样就能通过mq发出去了,如下:

//multifile是import org.springframework.web.multipart.MultipartFile;类型数据
//获取文件名称
String fileName = multifile.getOriginalFilename();
byte[] bytes=new byte[(int)multifile.getSize()];
InputStream is = multifile.getInputStream();
is.read(bytes);
//通过Base64讲字节数组转换为字符串
String fileContent = Base64.getEncoder().encodeToString(bytes);
//EmailFile这个对象存储文件名称和文件
EmailFile ef= new EmailFile();
ef.setFileName(fileName);
ef.setFileContent(fileContent);

以上是通过postman上传文件请求controller发送邮件,这里由于http限制,最多只能传10m以内的数据。

当然也可以直接上传本地文件,这样就没有大小限制的问题

//读取本地文件
FileReader fileReader = new FileReader("/Users/chenjianwen/Downloads/QQ_6.6.5.dmg");
//将文件转换成字节数组
byte[] result = fileReader.readBytes();
//通过Base64将字节数组转换成字符串
String fileContent = Base64.getEncoder().encodeToString(result);
EmailFile ef= new EmailFile();
ef.setFileName("QQ_6.6.5.dmg"); //自定义文件名称
ef.setFileContent(fileContent);

FileReader引入的是hutool工具类,其maven依赖如下:
 

<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.3.7</version>
</dependency>

Base64是java.util包中的,jdk自带的。

由于我邮件文件发送是通过InputStream发送的,所以我需要在mq监听器内将字符串的文件转换成InputStream

//multipartFile是我的文件对象
String fileName = multipartFile.getFileName(); //文件名称
String fileContent = multipartFile.getFileContent(); //文件,这里转换成了String类型
byte[] fileByte = Base64.decode(fileContent); //Base64降序列化
InputStream is = new ByteArrayInputStream(fileByte); //将字节转换成InputStream
sendEmail.send(email, content, template.getEmailTemplateName(), is, fileName); //发送邮件

这样,就可以通过我的邮件发送工具发送邮件了,如下

package cn.coralglobal.message.provider.core;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.security.Security;
import java.util.Properties;

import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.mail.*;
import javax.mail.internet.*;
import javax.mail.util.ByteArrayDataSource;

/**
 * @Description: 邮件发送
 * @Author chenjianwen
 * @Date 2020/4/13
 **/
@Slf4j
@Component
public class SendEmail {

    private static final String COMPANY_MARK = "这里写公司名称";


    /**
     *
     * @param email 邮箱
     * @param content 邮件内容
     * @param head 邮件名称
     * @param is 文件流信息
     * @param fileName 文件名称
     * @throws IOException
     */
    public void send(String email, String content, String head, InputStream is, String fileName) throws IOException {

        Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
        final String SSL_FACTORY = "javax.net.ssl.SSLSocketFactory";
        boolean b = false;
        String from = "公司邮箱";
        final String userName = "用户名";
        final String password = "密码";
        String host = "smtp.exmail.qq.com";

        Properties props = new Properties();
        props.put("mail.smtp.auth", "true");
        props.put("mail.smtp.starttls.enable", "true");
        props.put("mail.smtp.socketFactory.class", SSL_FACTORY);
        props.put("mail.smtp.socketFactory.fallback", "false");
        props.put("mail.smtp.host", host);
        props.put("mail.smtp.port", "465");// 根据http://help.163.com/10/0731/11/6CTUBPT300753VB8.html,非SSL协议的端口为25
        props.put("mail.smtp.socketFactory.port", "465");
        // 获取会话对象
        Session session = Session.getInstance(props, new Authenticator() {

            // Authenticator类是一个抽象类{
            // 这个getPasswordAuthentication()原本是return null的。
            // 需要重写这个方法,也是这个类里面最重要的一个方法。

            protected PasswordAuthentication getPasswordAuthentication() {
                return new PasswordAuthentication(userName, password);
            }
        });
        try {
            Message message = new MimeMessage(session);
            message.setFrom(new InternetAddress(from));
            message.setRecipients(Message.RecipientType.TO,
                    InternetAddress.parse(email));
            String subject = MimeUtility.encodeWord(this.COMPANY_MARK + head, "UTF-8", "Q");
            message.setSubject(subject);

            //创建Multipart对象,内部包含文件或内容
            Multipart multipart = new MimeMultipart();

            //发送文件
            if(is != null && !StringUtils.isEmpty(fileName)){
                MimeBodyPart fileBody = new MimeBodyPart();
                DataSource source = new ByteArrayDataSource(is, "application/msexcel");
                fileBody.setDataHandler(new DataHandler(source));
                fileBody.setFileName(MimeUtility.encodeText(fileName));
                multipart.addBodyPart(fileBody);
            }

            //发送内容
            MimeBodyPart text = new MimeBodyPart();
            text.setText(content,"UTF-8");
            text.setHeader("Content-Type", "text/html; charset=UTF-8");
            multipart.addBodyPart(text);

            message.setContent(multipart);
            message.saveChanges();

            Transport.send(message);
        } catch (MessagingException | UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }
}

邮件发送工具引入的包如下:
 

<dependency>
    <groupId>javax.mail</groupId>
    <artifactId>mail</artifactId>
    <version>1.4.7</version>
</dependency>

以上就是邮件发送文件的总结。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值