JSP 发送邮件

在 JSP 里用 JavaMail 发信、HTML 邮件与附件、常见失败原因。

Intermediate
Key PointsJSP 里发信与 Servlet 里完全一样:连 SMTP、认证、构造 MimeMessage;但发信应该异步,不要阻塞页面。

依赖

XML
<dependency>
  <groupId>jakarta.mail</groupId>
  <artifactId>jakarta.mail-api</artifactId>
  <version>2.1.3</version>
</dependency>
<dependency>
  <groupId>org.eclipse.angus</groupId>
  <artifactId>angus-mail</artifactId>
  <version>2.0.3</version>
  <scope>runtime</scope>
</dependency>

jakarta.mail-api is interfaces only; a runtime implementation (Angus Mail or the GlassFish one) is required. With the API alone you get Provider not found at runtime.

发一封纯文本邮件

JSP
<%@ page contentType="text/html;charset=UTF-8"
         import="jakarta.mail.*, jakarta.mail.internet.*, java.util.*" %>
<%
    Properties props = new Properties();
    props.put("mail.smtp.host", "smtp.example.com");
    props.put("mail.smtp.port", "587");
    props.put("mail.smtp.auth", "true");
    props.put("mail.smtp.starttls.enable", "true");

    final String user = System.getenv("MAIL_USER");
    final String pass = System.getenv("MAIL_PASS");

    Session mail = Session.getInstance(props, new Authenticator() {
        @Override
        protected PasswordAuthentication getPasswordAuthentication() {
            return new PasswordAuthentication(user, pass);
        }
    });

    Message msg = new MimeMessage(mail);
    msg.setFrom(new InternetAddress(user));
    msg.setRecipients(Message.RecipientType.TO,
            InternetAddress.parse(request.getParameter("to")));
    msg.setSubject("注册确认", "UTF-8");
    msg.setText("请点击链接完成注册。", "UTF-8");

    Transport.send(msg);
%>
<p>邮件已发送</p>

HTML 邮件与附件

JSP
MimeMessage msg = new MimeMessage(mail);
msg.setSubject("月度报告", "UTF-8");

Multipart mixed = new MimeMultipart("mixed");

MimeBodyPart text = new MimeBodyPart();
text.setText("您的客户端不支持 HTML。", "UTF-8");

MimeBodyPart html = new MimeBodyPart();
html.setContent("<h1>月度报告</h1><p>详见附件。</p>",
        "text/html;charset=UTF-8");

MimeMultipart alt = new MimeMultipart("alternative");
alt.addBodyPart(text);
alt.addBodyPart(html);

MimeBodyPart body = new MimeBodyPart();
body.setContent(alt);
mixed.addBodyPart(body);

MimeBodyPart file = new MimeBodyPart();
file.attachFile("/data/report.pdf");
file.setFileName(MimeUtility.encodeText("月度报告.pdf", "UTF-8", "B"));
mixed.addBodyPart(file);

msg.setContent(mixed);
Transport.send(msg);

alternative means the same content in several formats for the client to choose; mixed means parallel parts such as attachments. Reversing them shows two bodies plus an unreadable attachment name.

常见失败原因

SymptomCause
535 auth failed用了登录密码而非授权码
Connection timed out25 端口被云厂商封禁
550 relay denied发件人与认证账号域名不一致
Subject garbled未指定 UTF-8
Goes to spamSPF / DKIM / DMARC 未配
Sending synchronously in a JSP lets one slow SMTP server spin the page for ten seconds. Enqueue the job instead; the JSP only hands it off. Email must never gate a page.

账号密码放哪

Never hardcode credentials in a JSP — the source ships inside the war, can be decompiled, and may be committed to Git. Use environment variables, context-param or a JNDI resource supplied by the deployment.

Friends & Partners