固原市网站建设_网站建设公司_JavaScript_seo优化
2025/12/23 20:25:12 网站建设 项目流程

邮件群发工具实验报告

目录

  • 一、项目概述
  • 二、项目架构
  • 三、项目展示
    • 3.1 目录结构
    • 3.2 主程序(MailBulkSender)
    • 3.3 配置模块(MailConfig)
    • 3.4 数据读取模块(ExcelReader)
    • 3.5 模板渲染模块(TemplateRenderer)
    • 3.6 邮件发送模块(MailSender)
    • 3.7 实体类
  • 四、运行效果
    • 4.1 实际接收邮件
    • 4.2 单次邮件发送报告
    • 4.3 发送日志
  • 五、学习心得
  • 六、总结与后续规划

一、项目概述

本项目是一个基于Java的邮件批量发送工具,通过整合Excel数据读取、模板渲染和邮件发送功能,实现了个性化邮件的批量推送。项目采用模块化设计,核心技术栈包括POI(Excel处理)、JavaMail(邮件发送)、FreeMarker(模板引擎),并通过Maven进行依赖管理,确保开发过程的高效与规范。

二、项目架构

  1. 配置初始化:设置邮件服务器参数及发件人信息
  2. 数据导入:从Excel读取收件人列表
  3. 模板渲染:结合联系人信息生成个性化邮件内容
  4. 批量发送:多线程并发发送邮件,带重试机制
  5. 结果反馈:生成发送报告,统计成功/失败情况

三、项目展示

1.目录结构

屏幕截图 2025-12-23 185154

2. 主程序(MailBulkSender)

  • 串联各个模块,完成邮件群发的完整流程
package com;import com.bulk.config.MailConfig;
import com.bulk.entity.Contact;
import com.bulk.entity.SendLog;
import com.bulk.reader.ExcelReader;
import com.bulk.sender.MailSender;import java.util.List;public class MailBulkSender {public static void main(String[] args) {try {// ===================== 1. 配置邮件参数 =====================MailConfig mailConfig = new MailConfig();mailConfig.setSenderEmail("3407645223@qq.com"); // 发件人邮箱(如163/QQ)mailConfig.setSenderPassword("lblrwihhllzjdbed");  // 邮箱授权码(不是登录密码!)mailConfig.setSmtpHost("smtp.qq.com");          // SMTP服务器:163=smtp.163.com,QQ=smtp.qq.commailConfig.setSmtpPort(465);                      // 端口:163=25/465,QQ=587mailConfig.setSslEnable(true);                  // QQ邮箱需设为true,163默认false// ===================== 2. 读取Excel联系人 =====================ExcelReader excelReader = new ExcelReader();List<Contact> contacts = excelReader.readContacts("D:\\homework\\Java/contacts.xlsx"); // Excel文件路径System.out.println("读取到联系人数量:" + contacts.size());// ===================== 3. 初始化发送器并批量发送 =====================MailSender mailSender = new MailSender(mailConfig);// 邮件主题模板(支持变量替换)String subjectTemplate = "【${company}】尊敬的${name},这是一封个性化测试邮件";// 附件路径(无附件则传null)List<String> attachments = null;// 批量发送(模板名:resources/templates/email.ftl)List<SendLog> logs = mailSender.batchSend(contacts, "email.ftl", subjectTemplate, attachments);// ===================== 4. 生成发送报告 =====================mailSender.generateReport(logs, "D:\\homework\\Java/send-report.txt");} catch (Exception e) {e.printStackTrace();System.err.println("群发失败:" + e.getMessage());}}
}

3. 配置模块(MailConfig)

  • 管理邮件发送核心参数(发件人信息、SMTP服务器配置等)
  • 自动生成JavaMail所需的Properties配置
package com.bulk.config;import java.util.Properties;public class MailConfig {// 发件人邮箱private String senderEmail;// 发件人密码/授权码(QQ/163邮箱需用授权码)private String senderPassword;// SMTP服务器地址(QQ: smtp.qq.com; 163: smtp.163.com)private String smtpHost;// SMTP端口(QQ: 587; 163: 25/465)private int smtpPort;// 是否启用SSLprivate boolean sslEnable = false;// 构建JavaMail属性public Properties getMailProperties() {Properties props = new Properties();props.put("mail.smtp.host", smtpHost);props.put("mail.smtp.port", smtpPort);props.put("mail.smtp.auth", "true");props.put("mail.smtp.starttls.enable", "true");if (sslEnable) {props.put("mail.smtp.ssl.enable", "true");props.put("mail.smtp.ssl.trust", smtpHost);}return props;}// Getter & Setterpublic String getSenderEmail() { return senderEmail; }public void setSenderEmail(String senderEmail) { this.senderEmail = senderEmail; }public String getSenderPassword() { return senderPassword; }public void setSenderPassword(String senderPassword) { this.senderPassword = senderPassword; }public String getSmtpHost() { return smtpHost; }public void setSmtpHost(String smtpHost) { this.smtpHost = smtpHost; }public int getSmtpPort() { return smtpPort; }public void setSmtpPort(int smtpPort) { this.smtpPort = smtpPort; }public boolean isSslEnable() { return sslEnable; }public void setSslEnable(boolean sslEnable) { this.sslEnable = sslEnable; }
}

4. 数据读取模块(ExcelReader)

  • 解析Excel文件中的联系人信息(姓名、邮箱、公司)
  • 支持多种单元格类型解析,确保数据读取准确性
package com.bulk.reader;import com.bulk.entity.Contact;
import org.apache.poi.ss.usermodel.*;import java.io.File;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.List;public class ExcelReader {/*** 从Excel读取联系人信息(支持xlsx格式)* @param excelPath Excel文件路径* @return 联系人列表*/public List<Contact> readContacts(String excelPath) throws Exception {List<Contact> contacts = new ArrayList<>();FileInputStream fis = new FileInputStream(new File(excelPath));Workbook workbook = WorkbookFactory.create(fis);Sheet sheet = workbook.getSheetAt(0); // 读取第一个sheet// 跳过表头(第一行:姓名、邮箱、公司)for (int i = 1; i <= sheet.getLastRowNum(); i++) {Row row = sheet.getRow(i);if (row == null) continue;Contact contact = new Contact();// 第1列:姓名contact.setName(getCellValue(row.getCell(0)));// 第2列:邮箱contact.setEmail(getCellValue(row.getCell(1)));// 第3列:公司contact.setCompany(getCellValue(row.getCell(2)));// 校验邮箱非空if (contact.getEmail() != null && !contact.getEmail().isEmpty()) {contacts.add(contact);}}workbook.close();fis.close();return contacts;}private String getCellValue(Cell cell) {if (cell == null) return "";switch (cell.getCellType()) {case STRING:return cell.getStringCellValue().trim();case NUMERIC:return String.valueOf(cell.getNumericCellValue()).trim();case BOOLEAN:return String.valueOf(cell.getBooleanCellValue()).trim();default:return "";}}
}

5. 模板渲染模块(TemplateRenderer)

  • 基于FreeMarker实现邮件内容动态渲染
  • 支持变量替换,轻松实现个性化邮件内容
package com.bulk.renderer;import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;import java.io.StringWriter;
import java.util.Map;public class TemplateRenderer {private Configuration freemarkerConfig;public TemplateRenderer() {// 初始化FreeMarker配置freemarkerConfig = new Configuration(Configuration.VERSION_2_3_32);// 模板加载路径:resources/templates目录freemarkerConfig.setClassForTemplateLoading(this.getClass(), "/templates");freemarkerConfig.setDefaultEncoding("UTF-8");}/*** 渲染FreeMarker模板* @param templateName 模板文件名(如:email.ftl)* @param dataModel 模板变量(如:{name: "张三", company: "XX公司"})* @return 渲染后的文本*/public String render(String templateName, Map<String, Object> dataModel) throws Exception {Template template = freemarkerConfig.getTemplate(templateName);StringWriter writer = new StringWriter();try {template.process(dataModel, writer);} catch (TemplateException e) {throw new Exception("模板渲染失败:" + templateName, e);}return writer.toString();}
}

6. 邮件发送模块(MailSender)

  • 线程池并发发送,提升发送效率
  • 失败自动重试机制(最多3次),提高成功率
  • 详细记录发送日志,支持结果统计
package com.bulk.sender;import com.bulk.config.MailConfig;
import com.bulk.entity.Contact;
import com.bulk.entity.SendLog;
import com.bulk.renderer.TemplateRenderer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import javax.mail.*;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import java.io.FileWriter;
import java.util.*;
import java.util.concurrent.*;public class MailSender {private static final Logger logger = LoggerFactory.getLogger(MailSender.class);private final MailConfig mailConfig;private final TemplateRenderer templateRenderer;// 并发线程池(控制发送速度,避免被邮件服务器限制)private final ExecutorService executor = Executors.newFixedThreadPool(10);// 最大重试次数private static final int MAX_RETRY = 3;public MailSender(MailConfig mailConfig) {this.mailConfig = mailConfig;this.templateRenderer = new TemplateRenderer();}/*** 批量发送个性化邮件* @param contacts 收件人列表* @param templateName 邮件模板名(如:email.ftl)* @param subjectTemplate 主题模板(如:"【${company}】尊敬的${name},您好!")* @param attachmentPaths 附件路径列表(null则无附件)* @return 发送日志列表*/public List<SendLog> batchSend(List<Contact> contacts, String templateName,String subjectTemplate, List<String> attachmentPaths) {List<SendLog> logs = new CopyOnWriteArrayList<>();for (Contact contact : contacts) {SendLog log = new SendLog();log.setContact(contact);log.setSendTime(new Date());logs.add(log);// 异步发送(避免阻塞)executor.submit(() -> {int retryCount = 0;boolean sendSuccess = false;String errorMsg = "";// 失败重试逻辑while (retryCount < MAX_RETRY && !sendSuccess) {try {// 构建模板变量Map<String, Object> dataModel = new HashMap<>();dataModel.put("name", contact.getName());dataModel.put("company", contact.getCompany());dataModel.put("email", contact.getEmail());// 渲染邮件主题和内容String subject = renderSimpleTemplate(subjectTemplate, dataModel);String content = templateRenderer.render(templateName, dataModel);// 发送单封邮件sendSingleMail(contact.getEmail(), subject, content, attachmentPaths);sendSuccess = true;logger.info("邮件发送成功:{}", contact.getEmail());} catch (Exception e) {retryCount++;errorMsg = e.getMessage();logger.error("发送失败(重试{}次):{},原因:{}", retryCount, contact.getEmail(), e.getMessage());// 重试间隔(指数退避:1s、2s、3s)try {Thread.sleep(1000 * retryCount);} catch (InterruptedException ie) {Thread.currentThread().interrupt();}}}// 更新发送日志log.setSuccess(sendSuccess);log.setErrorMsg(errorMsg);log.setRetryCount(retryCount);log.setSubject(subjectTemplate);});}// 等待所有任务完成executor.shutdown();try {if (!executor.awaitTermination(1, TimeUnit.HOURS)) {executor.shutdownNow();}} catch (InterruptedException e) {executor.shutdownNow();Thread.currentThread().interrupt();}return logs;}/*** 发送单封邮件(支持附件)*/private void sendSingleMail(String toEmail, String subject, String content, List<String> attachmentPaths) throws Exception {Properties props = mailConfig.getMailProperties();// 创建邮件会话(带认证)Session session = Session.getInstance(props, new Authenticator() {@Overrideprotected PasswordAuthentication getPasswordAuthentication() {return new PasswordAuthentication(mailConfig.getSenderEmail(),mailConfig.getSenderPassword());}});session.setDebug(false); // 关闭调试日志// 构建邮件消息MimeMessage message = new MimeMessage(session);message.setFrom(new InternetAddress(mailConfig.getSenderEmail()));message.setRecipient(Message.RecipientType.TO, new InternetAddress(toEmail));message.setSubject(subject, "UTF-8");message.setContent(content, "text/html;charset=UTF-8");message.setSentDate(new Date());// 【可选】添加附件(如需附件,取消注释并完善)/*if (attachmentPaths != null && !attachmentPaths.isEmpty()) {MimeMultipart multipart = new MimeMultipart();// 邮件正文部分BodyPart contentPart = new MimeBodyPart();contentPart.setContent(content, "text/html;charset=UTF-8");multipart.addBodyPart(contentPart);// 附件部分for (String path : attachmentPaths) {MimeBodyPart attachmentPart = new MimeBodyPart();FileDataSource dataSource = new FileDataSource(path);attachmentPart.setDataHandler(new DataHandler(dataSource));attachmentPart.setFileName(MimeUtility.encodeText(dataSource.getName()));multipart.addBodyPart(attachmentPart);}message.setContent(multipart);}*/// 发送邮件Transport.send(message);}/*** 简易模板渲染(用于邮件主题)*/private String renderSimpleTemplate(String template, Map<String, Object> dataModel) {String result = template;for (Map.Entry<String, Object> entry : dataModel.entrySet()) {result = result.replace("${" + entry.getKey() + "}", entry.getValue().toString());}return result;}/*** 生成发送报告(控制台打印 + 可扩展写入文件)*/public void generateReport(List<SendLog> logs, String reportPath) {long successCount = logs.stream().filter(SendLog::isSuccess).count();long failCount = logs.size() - successCount;logger.info("===== 邮件发送报告 =====");logger.info("总发送数:{}", logs.size());logger.info("成功数:{}", successCount);logger.info("失败数:{}", failCount);logger.info("========================");// 【可选】将日志写入文件(如Excel/TXT)try (FileWriter writer = new FileWriter(reportPath)) {writer.write("姓名,邮箱,公司,是否成功,错误信息,重试次数,发送时间\n");for (SendLog log : logs) {writer.write(String.format("%s,%s,%s,%s,%s,%d,%s\n",log.getContact().getName(),log.getContact().getEmail(),log.getContact().getCompany(),log.isSuccess(),log.getErrorMsg(),log.getRetryCount(),log.getSendTime()));}} catch (Exception e) {logger.error("生成报告失败", e);}}
}

7. 实体类

  • Contact:存储联系人基础信息
package com.bulk.entity;public class Contact {private String name;      // 姓名private String email;     // 邮箱private String company;   // 公司// Getter & Setterpublic String getName() { return name; }public void setName(String name) { this.name = name; }public String getEmail() { return email; }public void setEmail(String email) { this.email = email; }public String getCompany() { return company; }public void setCompany(String company) { this.company = company; }
}
  • SendLog:记录每封邮件的发送结果
package com.bulk.entity;import java.util.Date;public class SendLog {private Contact contact;      // 收件人private String subject;       // 邮件主题private boolean success;      // 是否发送成功private String errorMsg;      // 错误信息(失败时)private int retryCount;       // 重试次数private Date sendTime;        // 发送时间// Getter & Setterpublic Contact getContact() { return contact; }public void setContact(Contact contact) { this.contact = contact; }public String getSubject() { return subject; }public void setSubject(String subject) { this.subject = subject; }public boolean isSuccess() { return success; }public void setSuccess(boolean success) { this.success = success; }public String getErrorMsg() { return errorMsg; }public void setErrorMsg(String errorMsg) { this.errorMsg = errorMsg; }public int getRetryCount() { return retryCount; }public void setRetryCount(int retryCount) { this.retryCount = retryCount; }public Date getSendTime() { return sendTime; }public void setSendTime(Date sendTime) { this.sendTime = sendTime; }
}

四、运行效果

1.实际接收邮件

屏幕截图 2025-12-23 185058
2.单次邮件发送报告

屏幕截图 2025-12-23 184600
3.发送日志

屏幕截图 2025-12-23 191404

五、学习心得

做这个邮件群发项目让我们特别有感触,让邮件成功发送出去总会遇到各种问题,在编程过程中,加强了排查问题的能力,还有利用AI引入FreeMarker模板引擎,不用硬编码写邮件内容,改改变量就能给不同人发个性化邮件,省了超多事。

六、总结

本项目实现了邮件群发的基本功能,后续考虑添加以下功能并作为课设:

  • 图形化界面,简化操作流程
  • 邮件接收功能
  • 邮件内容在线编辑功能
  • 其他(暂时没想到)

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询