给用户发送带附件的邮件,附件为本地资源或者可访问URL资源。
由于我们是定时任务发送邮件,突然发现发送邮件的线程处于无限等待的状态。导致新申请的邮件一直发送不出去。
直接上代码。核心代码,可自行封装优化,测试
@Autowired
private JavaMailSender mailSender;
@Autowired
private MailProperties mailProperties;
public boolean sendAttachmentsMail(String to, String subject, String content, String filePath, String fileName) {
MimeMessage message = mailSender.createMimeMessage();
try {
MimeMessageHelper helper = new MimeMessageHelper(message, true);
// 指定from 获取 配置文件中的userName
String username = mailProperties.getUsername();
helper.setFrom(new InternetAddress(username));
helper.setTo(to);
helper.setSubject(subject);
helper.setText(content, true);
//如果是本地文件 File file = new File("/Users/a.txt");
//helper.addAttachment(fileName, new FileSystemResource(file));
//远程文件 可访问URL文件 使用getMultipart()
message.setContent(getMultipart(content, filePath, fileName));
mailSender.send(message);
log.info("带附件的邮件发送成功");
return true;
} catch (Exception e) {
log.error("带附件的邮件发送失败,异常信息:", e);
return false;
}
}
/**
* 获取Multipart对象,包含正文内容、附件、文件名
*/
private static Multipart getMultipart(String content, String remoteFile, String fileName) throws Exception {
Multipart multipart = new MimeMultipart();
// 向multipart中添加正文
BodyPart contentBodyPart = new MimeBodyPart();
contentBodyPart.setContent(content, "text/html;charset=UTF-8");
multipart.addBodyPart(contentBodyPart);
// 向multipart中添加远程附件
multipart.addBodyPart(getBodyPart(encodeUri(remoteFile), fileName));
return multipart;
}
/**
* 对文件fileUrl进行编码 类似于浏览器的encodeURI编码 例子:编码前:http://www.baidu.com/api/resource/robot/word/2/婚前协议.doc
* 编码后:http://www.baidu.com/api/resource/robot/word/2/%E5%A9%9A%E5%89%8D%E5%8D%8F%E8%AE%AE.doc
*/
public static String encodeUri(String url) throws UnsupportedEncodingException {
String encode = URLEncoder.encode(url, "UTF-8");
return encode.replace("%3A", ":").replace("%2F", "/");
}
/**
* 获取 bodyPart
*
* @param path 文件路径
* @param fileName 文件名称
*/
public static BodyPart getBodyPart(String path, String fileName) throws Exception {
BodyPart bodyPart = new MimeBodyPart();
// 根据附件路径获取文件,
DataHandler dh = new DataHandler(new URLDataSource(new URL(path)));
bodyPart.setDataHandler(dh);
bodyPart.setFileName(fileName);
return bodyPart;
}
spring:
mail:
protocol: smtps
port: 465
host: smtp.mxhichina.com
username: XXX
password: XXX
default-encoding: UTF-8
properties:
mail:
smtp:
connectiontimeout: 5000
timeout: 5000
writetimeout: 5000
auth: true
starttls:
required: true
enable: true
:重点解决方案:
配置文件中的超时时间是重点。以下内容摘自spring 官方文档。
- Sending Email
The Spring Framework provides an easy abstraction for sending email by using the JavaMailSender interface, and Spring Boot provides auto-configuration for it as well as a starter module.
See the reference documentation.
for a detailed explanation of how you can use JavaMailSender.
If spring.mail.host and the relevant libraries (as defined by spring-boot-starter-mail) are available, a default JavaMailSender is created if none exists. The sender can be further customized by configuration items from the spring.mail namespace. See MailProperties for more details.
In particular, certain default timeout values are infinite, and you may want to change that to avoid having a thread blocked by an unresponsive mail server, as shown in the following example:
spring.mail.properties.mail.smtp.connectiontimeout=5000
spring.mail.properties.mail.smtp.timeout=3000
spring.mail.properties.mail.smtp.writetimeout=5000
In particular, certain default timeout values are infinite
特别是,某些默认超时值是无限的.这是个坑。/糗大了
更新一下:2021-09-14
上面的配置文件不生效:依赖版本如下
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-mailartifactId>
<version>2.2.5.RELEASEversion>
dependency>
yaml 配置如下(不生效):
spring:
mail:
protocol: smtps
port: 465
host: smtp.mxhichina.com
username: XXX
password: XXX
default-encoding: UTF-8
properties:
mail:
smtp:
connectiontimeout: 5000
timeout: 5000
writetimeout: 5000
auth: true
starttls:
required: true
enable: true
点击:spring最新文档链接
spring: mail: properties: "[mail.smtp.connectiontimeout]": 5000 "[mail.smtp.timeout]": 3000 "[mail.smtp.writetimeout]": 5000
这么写就没问题了。注意依赖版本哦。
package org.springframework.boot.autoconfigure.mail;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Configuration properties for email support.
*
* @author Oliver Gierke
* @author Stephane Nicoll
* @author Eddú Meléndez
* @since 1.2.0
*/
@ConfigurationProperties(prefix = "spring.mail")
public class MailProperties {
private static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
/**
* SMTP server host. For instance, `smtp.example.com`.
*/
private String host;
/**
* SMTP server port.
*/
private Integer port;
/**
* Login user of the SMTP server.
*/
private String username;
/**
* Login password of the SMTP server.
*/
private String password;
/**
* Protocol used by the SMTP server.
*/
private String protocol = "smtp";
/**
* Default MimeMessage encoding.
*/
private Charset defaultEncoding = DEFAULT_CHARSET;
/**
* Additional JavaMail Session properties.
*/
private Map<String, String> properties = new HashMap<>();
/**
* Session JNDI name. When set, takes precedence over other Session settings.
*/
private String jndiName;