掌上游戏新活动平台

针对QQ邮箱发邮件限制的解决方案

2025-09-27 20:42:24

由于QQ邮箱对于SMTP服务发送邮件做了限制,每分钟发送40封之后会被限制不能再发送,对于这样的限制又需要发送大量邮件的时候的解决方案如下

使用多个邮箱轮换使用进行发送

1、将使用的邮箱存储在一个统一的字符串变量中,将所有可使用的邮箱存储在一个字符串数组中(我的三个邮箱授权码相同,如果授权码不同,则建立一个授权码数组,和邮箱切换的解决方案同理)

全局变量(发邮件使用的邮箱)

private static String FPAMail="freeprogramming@qq.com";

可使用的邮箱数组

private static String FPAMailArray[]={"freeprogramming@qq.com","humorchen@vip.qq.com","3301633914@qq.com"};

2、将建立连接的代码封装到一个函数,将连接对象变为成员变量,全局化,即每次调用同一个变量,而变量的对象可能不相同(会变化)

连接相关对象变为全局变量

private static Properties props;

private static Session mailSession;

private static MimeMessage message;

private static Transport transport;

建立连接的函数

private void init()

{

System.out.println("QQ邮件服务初始化开始:账号"+FPAMail);

Date start=new Date();

try {

// 创建Properties 类用于记录邮箱的一些属性

props = new Properties();

// 表示SMTP发送邮件,必须进行身份验证

props.put("mail.smtp.auth", "true");

//此处填写SMTP服务器

props.put("mail.smtp.host", "smtp.qq.com");

//端口号,QQ邮箱给出了两个端口,但是另一个我一直使用不了,所以就给出这一个587

props.put("mail.smtp.port", "587");

// 此处填写你的账号

props.put("mail.user",FPAMail );

// 此处的密码就是前面说的16位STMP口令

props.put("mail.password", FPAMailPwd);

// 构建授权信息,用于进行SMTP进行身份验证

Authenticator authenticator = new Authenticator() {

protected PasswordAuthentication getPasswordAuthentication() {

// 用户名、密码

String userName = props.getProperty("mail.user");

String password = props.getProperty("mail.password");

return new PasswordAuthentication(userName, password);

}

};

// 使用环境属性和授权信息,创建邮件会话

mailSession = Session.getInstance(props, authenticator);// 创建邮件消息

message = new MimeMessage(mailSession);

// 设置发件人

InternetAddress form = new InternetAddress(

props.getProperty("mail.user"),NickName,"utf-8");

message.setFrom(form);

}catch (Exception e)

{

e.printStackTrace();

}

Date end=new Date();

System.out.println("QQ邮件发送会话初始化成功,耗时"+((end.getTime()-start.getTime()))+"毫秒");

}

(不懂这几个对象是干嘛的百度)

3、设置每发送20封邮件切换一次邮箱,封装成函数

函数如下:

private void switchMail()

{

int i=0;

for (;i

if (FPAMail.equals(FPAMailArray[i]))

break;

if (i+1==FPAMailArray.length)

i=0;

else

i++;

FPAMail=FPAMailArray[i];

init();

}

4、每次发送邮件的时候做判断(i%20==0)

public void sendToAllMember(String title,String html_content)

{

System.out.println("发送邮件给所有会员");

int i=1;

for (String mail: MemberQQMailData.mails)

{

System.out.println("正在处理第"+(i++)+"个"+"剩余"+(MemberQQMailData.mails.length-i+1)+"个,正在发送给:"+mail);

sendQQMail(title,MailContentGenerator.QQMailNotice(title,html_content),mail);

if (i%20==0)

switchMail();

}

}

(MemberQQMailData.mails是一个字符串数组,存有所有会员的QQ邮箱)

效果图:每到第20封邮件就换一个邮箱进行发送,完美解决QQ邮箱发送限制问题

新版解决方案代码工具类

新版工具类代码

package cn.freeprogramming.util;

import cn.hutool.core.date.StopWatch;

import lombok.extern.slf4j.Slf4j;

import org.springframework.scheduling.annotation.Async;

import javax.mail.*;

import javax.mail.internet.InternetAddress;

import javax.mail.internet.MimeMessage;

import java.util.ArrayList;

import java.util.List;

import java.util.Objects;

import java.util.Properties;

import java.util.concurrent.locks.ReentrantLock;

/**

* QQ邮件发送工具

*

* @Author:humorchen

* @Date 2022/1/3 20:50

*/

@Slf4j

public class QQMailUtil {

/**

* 超时时间

*/

private long TIMEOUT = 5000;

/**

* 重试次数

*/

private int RETRY_LIMIT = 10;

/**

* 使用第多少个账号(QQ每个账号限制频率了)

*/

private int accountIndex = 0;

/**

* 锁

*/

private ReentrantLock lock = new ReentrantLock(true);

/**

* 账号列表

*/

private List accountList = new ArrayList<>();

/**

* 配置列表

*/

private List propertiesList = new ArrayList<>();

/**

* 授权器

*/

private List authenticatorList = new ArrayList<>();

/**

* 会话列表

*/

private List sessionList = new ArrayList<>();

/**

* 消息对象列表

*/

private List messageList = new ArrayList<>();

/**

* 发件人地址信息列表

*/

private List internetAddressArrayList = new ArrayList<>();

/**

* 发送器列表

*/

private List transportList = new ArrayList<>();

/**

* 切换发件使用的邮箱下标

*/

private void changeUsingAccountIndex() {

if (accountIndex == accountList.size() - 1) {

accountIndex = 0;

} else {

accountIndex++;

}

}

/**

* 与QQ邮箱服务器建立连接

*

* @throws Exception

*/

private void connect() throws Exception {

QQMailAccount qqMailAccount = accountList.get(accountIndex);

Properties properties = propertiesList.get(accountIndex);

Authenticator authenticator = authenticatorList.get(accountIndex);

Session session = Session.getInstance(properties, authenticator);

MimeMessage message = new MimeMessage(session);

InternetAddress senderInternetAddress = internetAddressArrayList.get(accountIndex);

// 设置发件人

InternetAddress fromInternetAddress = new InternetAddress(

qqMailAccount.getAccount(), qqMailAccount.getNickname(), "utf-8");

message.setFrom(fromInternetAddress);

sessionList.set(accountIndex, session);

messageList.set(accountIndex, message);

Transport transport = session.getTransport(senderInternetAddress);

transport.connect();

log.info("isConnected {}", transport.isConnected());

if (accountIndex < transportList.size()) {

transportList.set(accountIndex, transport);

} else {

transportList.add(transport);

}

}

/**

* 发送邮件方法

* 由线程池处理

*

* @param title 邮件标题

* @param html_content 邮件内容(支持html,图片等内容可能会被拦截,需要用户点击查看才能看到,或者让用户设置信任这个邮箱)

* @param receiver 收件人邮箱

*/

@Async

public void sendQQMail(String title, String html_content, String receiver) {

StopWatch stopWatch = new StopWatch();

try {

lock.lock();

log.info("发送邮件给 {} ,标题:{} ,\n内容:{}", receiver, title, html_content);

stopWatch.start();

QQMailAccount qqMailAccount = accountList.get(accountIndex);

Transport transport = transportList.get(accountIndex);

MimeMessage message = messageList.get(accountIndex);

if (transport == null || !transport.isConnected()) {

connect();

}

stopWatch.stop();

log.info("连接花费 {}ms", stopWatch.getTotalTimeMillis());

stopWatch.start();

// 设置收件人的邮箱

message.setRecipient(Message.RecipientType.TO, new InternetAddress(receiver));

// 设置邮件标题

message.setSubject(title, "utf-8");

// 设置邮件的内容体

message.setContent(html_content, "text/html;charset=UTF-8");

try {

//保存修改

message.saveChanges();

//发送邮件

transport.sendMessage(message, new InternetAddress[]{new InternetAddress(receiver)});

stopWatch.stop();

log.info("使用邮箱:{} 发送成功,花费时间:{}ms", qqMailAccount.getAccount(), stopWatch.getTotalTimeMillis());

} catch (Exception e) {

//由于被腾讯方面因超时被关闭连接属于正常情况

log.info("邮件发送失败,正在尝试和QQ邮件服务器重新建立链接");

stopWatch.stop();

stopWatch.start();

boolean success = false;

for (int i = 1; i <= RETRY_LIMIT; i++) {

try {

connect();

log.info("使用邮箱:{} 成功建立链接", qqMailAccount.getAccount());

transport = transportList.get(accountIndex);

message = messageList.get(accountIndex);

success = true;

break;

} catch (Exception ee) {

changeUsingAccountIndex();

qqMailAccount = accountList.get(accountIndex);

log.info("链接建立失败,切换到邮箱:{} ,进行第 {} 次重试..." + qqMailAccount.getAccount(), i);

}

}

if (success) {

// 设置收件人的邮箱

message.setRecipient(Message.RecipientType.TO, new InternetAddress(receiver));

// 设置邮件标题

message.setSubject(title, "utf-8");

// 设置邮件的内容体

message.setContent(html_content, "text/html;charset=UTF-8");

message.saveChanges();

transport.sendMessage(message, new InternetAddress[]{new InternetAddress(receiver)});

stopWatch.stop();

log.info("重建连接后使用邮箱:{} 发送成功,耗费时间:{}ms", qqMailAccount.getAccount(), stopWatch.getTotalTimeMillis());

} else {

log.error("链接多次尝试后无法建立,邮件发送失败!");

return;

}

}

} catch (Exception e) {

log.error("sendQQMail", e);

} finally {

lock.unlock();

if (stopWatch.isRunning()) {

stopWatch.stop();

}

}

}

/**

* 添加账号

*/

public void addAccount(String account, String authorizationCode, String nickname) {

int oldAccountIndex = accountIndex;

boolean addFinished = false;

try {

lock.lock();

oldAccountIndex = accountIndex;

accountIndex = accountList.size();

QQMailAccount qqMailAccount = new QQMailAccount(account, authorizationCode, nickname);

Properties properties = createProperties(qqMailAccount);

Authenticator authenticator = createAuthenticator(qqMailAccount);

// 使用环境属性和授权信息,创建邮件会话

Session session = Session.getInstance(properties, authenticator);

MimeMessage message = new MimeMessage(session);

// 设置发件人

InternetAddress internetAddress = new InternetAddress(

account, nickname, "utf-8");

message.setFrom(internetAddress);

Transport transport = session.getTransport(new InternetAddress(qqMailAccount.getAccount()));

transport.connect();

accountList.add(qqMailAccount);

propertiesList.add(properties);

authenticatorList.add(authenticator);

sessionList.add(session);

transportList.add(transport);

messageList.add(message);

internetAddressArrayList.add(internetAddress);

addFinished = true;

} catch (Exception e) {

//移除已经加入的

if (addFinished) {

accountList.remove(accountIndex);

propertiesList.remove(accountIndex);

authenticatorList.remove(accountIndex);

sessionList.remove(accountIndex);

transportList.remove(accountIndex);

messageList.remove(accountIndex);

internetAddressArrayList.remove(accountIndex);

}

log.error("addAccount", e);

} finally {

accountIndex = oldAccountIndex;

lock.unlock();

}

}

/**

* 创建配置文件

*

* @param qqMailAccount

* @return

*/

private Properties createProperties(QQMailAccount qqMailAccount) {

// 创建Properties 类用于记录邮箱的一些属性

Properties properties = new Properties();

// 表示SMTP发送邮件,必须进行身份验证

properties.put("mail.smtp.auth", "true");

//此处填写SMTP服务器

properties.put("mail.smtp.host", "smtp.qq.com");

//端口号,QQ邮箱给出了两个端口,但是另一个我一直使用不了,所以就给出这一个587

properties.put("mail.smtp.port", "587");

// 此处填写你的账号

properties.put("mail.user", qqMailAccount.getAccount());

// 此处的密码就是前面说的16位STMP口令

properties.put("mail.password", qqMailAccount.getAuthorizationCode());

//设置超时时间

properties.put("mail.smtp.timeout", "" + TIMEOUT);

return properties;

}

/**

* 创建授权信息对象

*

* @param qqMailAccount

* @return

*/

private Authenticator createAuthenticator(QQMailAccount qqMailAccount) {

// 构建授权信息,用于进行SMTP进行身份验证

Authenticator authenticator = new Authenticator() {

@Override

protected PasswordAuthentication getPasswordAuthentication() {

// 用户名、密码

String userName = qqMailAccount.getAccount();

String password = qqMailAccount.getAuthorizationCode();

return new PasswordAuthentication(userName, password);

}

};

return authenticator;

}

private static class QQMailAccount {

/**

* 账号

*/

private String account;

/**

* 授权码

*/

private String authorizationCode;

/**

* 发送者昵称

*/

private String nickname;

public QQMailAccount(String account, String authorizationCode, String nickname) {

this.account = account;

this.authorizationCode = authorizationCode;

this.nickname = nickname;

}

public String getAccount() {

return account;

}

public void setAccount(String account) {

this.account = account;

}

public String getAuthorizationCode() {

return authorizationCode;

}

public void setAuthorizationCode(String authorizationCode) {

this.authorizationCode = authorizationCode;

}

public String getNickname() {

return nickname;

}

public void setNickname(String nickname) {

this.nickname = nickname;

}

@Override

public boolean equals(Object o) {

if (this == o) return true;

if (o == null || getClass() != o.getClass()) return false;

QQMailAccount that = (QQMailAccount) o;

return Objects.equals(account, that.account) && Objects.equals(authorizationCode, that.authorizationCode) && Objects.equals(nickname, that.nickname);

}

@Override

public int hashCode() {

return Objects.hash(account, authorizationCode, nickname);

}

}

}

配置工具类对象到容器

package cn.freeprogramming.config;

import cn.freeprogramming.util.QQMailUtil;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

/**

* QQ邮件配置

*

* @Author:humorchen

* @Date 2022/1/3 21:54

*/

@Configuration

public class QQMailConfig {

//授权码,在QQ邮箱里设置

private String authorizationCode = "hkkm123kasbjbf";

private String nickname = "自由编程协会";

@Bean

public QQMailUtil qqMailUtil() {

QQMailUtil qqMailUtil = new QQMailUtil();

qqMailUtil.addAccount("freeprogramming@qq.com", authorizationCode, nickname);

qqMailUtil.addAccount("freeprogramming@foxmail.com", authorizationCode, nickname);

qqMailUtil.addAccount("357341307@qq.com", authorizationCode, nickname);

return qqMailUtil;

}

}