javaEE(17)_邮件原理与JavaMail开发

一、Java邮件开发介绍

为什么要学习javamail开发
•现在很多WEB应用在开发时都需要集成邮件发送功能,例如:
•给新注册的用户自动发送一封包含其注册信息的欢迎E-Mail.
•给过生日的注册会员自动发送一封表示祝贺的E-Mail .
•将网站的最新活动信息通过E-Mail发送给所有的注册会员.
•等等 …...
•这些功能的实现都需要软件开发人员在开发WEB应用中编写相应的邮件处理程序.

二、邮件开发涉到的一些基本概念

1、邮件服务器和电子邮箱
邮件服务器:
•要在Internet上提供电子邮件功能,必须有专门的电子邮件服务器.例如现在Internet很多提供邮件服务的厂商:sina、sohu、163等等他们都有自己的邮件服务器.这些服务器类似于现实生活中的邮局,它主要负责接收用户投递过来的邮件,并把邮件投递到邮件接收者的电子邮箱中.
电子邮箱:
•电子邮箱(E-mail地址)的获得需要在邮件服务器上进行申请 ,确切地说,电子邮箱其实就是用户在邮件服务器上申请的一个帐户.用户在邮件服务器上申请了一个帐号后,邮件服务器就会为这个帐号分配一定的空间,用户从而可以使用这个帐号以及空间,发送电子邮件和保存别人发送过来的电子邮件.
2、邮件传输协议
SMTP协议
•用户连上邮件服务器后,要想给它发送一封电子邮件,需要遵循一定的通迅规则,SMTP协议就是用于定义这种通讯规则的.
•因而,通常我们也把处理用户smtp请求(邮件发送请求)的邮件服务器称之为SMTP服务器.(25)
POP3协议
•同样,用户若想从邮件服务器管理的电子邮箱中接收一封电子邮件的话,他连上邮件服务器后,也需要遵循一定的通迅格式,POP3协议用于定义这种通讯格式.
•因而,通常我们也把处理用户pop3请求(邮件接收请求)的邮件服务器称之为POP3服务器.(110)
3、电子邮件的传输过程
下图用于演示[email protected][email protected]帐户相互发送邮件的过程.

三、手工演示电子邮件的发送

准备实验环境
•提前在sohu和sina上注册一个免费帐号
•获取smtp和pop3服务器的名称
•Sina或sohu会在其帮助中心里提示管理当前注册帐号的邮件服务器的主机名.
•编写base64编码程序对用户名和密码进行编码.
•手工使用SMTP协议发送电子邮件

整个流程如下:

telnet localhost 25 //telnet 可连接互联网任意一台主机 ehlo flx auth login YWFh MTIz mail from: <[email protected]> rcpt to: <[email protected]> data from: <[email protected]> to: <[email protected]> subject: test 美令,咱们分手吧 中正 . quit telnet localhost 110 user aaa pass 123 stat list retr 2 quit

四、编写Socket程序发送电子邮件

public class Demo1 { 
    public static void main(String[] args) throws Exception { 
        Socket socket = new Socket("localhost",25); BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream())); OutputStream out = socket.getOutputStream(); System.out.println(br.readLine()); out.write("ehlo flx\r\n".getBytes()); System.out.println(br.readLine()); System.out.println(br.readLine()); out.write("auth login\r\n".getBytes()); System.out.println(br.readLine()); out.write("YWFh\r\n".getBytes()); System.out.println(br.readLine()); out.write("MTIz\r\n".getBytes()); System.out.println(br.readLine()); out.write("mail from: <[email protected]>\r\n".getBytes()); System.out.println(br.readLine()); out.write("rcpt to: <[email protected]>\r\n".getBytes()); System.out.println(br.readLine()); out.write("data\r\n".getBytes()); System.out.println(br.readLine()); out.write("from:<[email protected]>\r\nto:<[email protected]>\r\nsubject:test\r\n".getBytes()); out.write(".\r\n".getBytes()); System.out.println(br.readLine()); out.write("quit\r\n".getBytes()); System.out.println(br.readLine()); br.close(); out.close(); socket.close(); } }

五、创建邮件—— MIME协议

•MIME协议是对RFC822文档的升级和补充,它描述了如何生成一封复杂邮件.通常我们把MIME协议描述的邮件称之为MIME邮件,MIME协议描述的数据称之为MIME消息.
•对于一封复杂邮件,如果包含了多个不同的数据,MIME协议规定了要使用分隔线对多段数据进行分隔,并使用Content-Type头字段对数据的类型、以及多个数据之间的关系进行描述.

MIME协议常用头字段

Content-type:字段

•数据类型 
•以"主类型/子类型"的形式出现,主类型有text、image、audio、video、application、message等,分别表示文本、图片、音频、视频、应用程序、组合结构、消息等.每个主类型下面都有多个子类型,例如text主类型包含plain、html、xml、css等子类型.
•数据的关系
•multipart/mixed、multipart/related、multipart/alternative

Content-Disposition头字段

•Content-Disposition头字段用于指定邮件阅读程序处理数据内容的方式,有inline和attachment两种标准方式,inline表示直接处理,而attachment表示当作附件处理.如果将Content-Disposition设置为attachment,在其后还可以指定filename属性,如下所示:
Content-Disposition: attachment; filename="1.bmp"
上面的MIME头字段表示MIME消息体的内容为邮件附件,附件名"1.bmp"

Content-ID头字段

•Content-ID头字段用于为"multipart/related"组合消息中的内嵌资源指定一个唯一标识号,在HTML格式的正文中可以使用这个唯一标识号来引用该内嵌资源.例如,假设将一个表示内嵌图片的MIME消息的Content-ID头字段设置为如下形式:
Content-ID: it315logo_gif
那么,在HTML正文中就需要使用如下HTML语句来引用该图片资源:
<img src="cid:it315logo_gif"> 
注意,在引用Content-ID头字段标识的内嵌资源时,要在资源的唯一标识号前面加上"cid:",以说明要采用唯一标识号对资源进行引用.

MIME协议例:

//创建简单邮件
public class SimpleMail { public static void main(String[] args) throws Exception { Session session = Session.getDefaultInstance(new Properties()); MimeMessage message = new MimeMessage(session); message.setFrom(new InternetAddress("[email protected]")); message.setRecipient(Message.RecipientType.TO, new InternetAddress("[email protected]")); message.setSubject("test"); message.setContent("aaaaaaaaaaaa", "text/html"); message.saveChanges(); message.writeTo(new FileOutputStream("c:\\1.eml")); } }
//创建带图片邮件
public class ImageMail { public static void main(String[] args) throws Exception { Session session = Session.getDefaultInstance(new Properties()); MimeMessage message = new MimeMessage(session); message.setFrom(new InternetAddress("[email protected]")); message.setRecipient(Message.RecipientType.TO, new InternetAddress("[email protected]")); message.setSubject("test"); //创建邮件中的数据 //创建正文
        MimeBodyPart text = new MimeBodyPart(); text.setContent("aaaa<br/><img src='cid:1.jpg'><br/>aaaaaaaa", "text/html"); //创建图片,不需要再setContent,setDataHandler可以感知类型
        MimeBodyPart image = new MimeBodyPart(); DataHandler dh = new DataHandler(new FileDataSource("src/1.jpg"));//jaf技术,把文件作为流
 image.setDataHandler(dh); image.setContentID("1.jpg"); //描述数据之间的关系
        MimeMultipart mm = new MimeMultipart(); mm.addBodyPart(text); mm.addBodyPart(image); mm.setSubType("related"); message.setContent(mm); message.saveChanges(); message.writeTo(new FileOutputStream("c:\\1.eml")); } }
//创建带附件的邮件
public class AttachMail { public static void main(String[] args) throws Exception { Session session = Session.getDefaultInstance(new Properties()); MimeMessage message = new MimeMessage(session); message.setFrom(new InternetAddress("[email protected]")); message.setRecipient(Message.RecipientType.TO, new InternetAddress("[email protected]")); message.setSubject("test"); //创建封装正文数据的bodypart
        MimeBodyPart text = new MimeBodyPart(); text.setContent("aaaaaaaaaaaa", "text/html"); //创建封装附件数据的bodypart
        MimeBodyPart attach = new MimeBodyPart(); DataHandler dh = new DataHandler(new FileDataSource("src/1.mp3")); attach.setDataHandler(dh); attach.setFileName(dh.getName()); //设置附件名 //mixed
        MimeMultipart mm = new MimeMultipart(); mm.addBodyPart(text); mm.addBodyPart(attach); mm.setSubType("mixed"); message.setContent(mm); message.saveChanges(); message.writeTo(new FileOutputStream("c:\\1.eml")); } }
//创建最复杂邮件
public class ComplexMail { public static void main(String[] args) throws Exception { //创建邮件
        Session session = Session.getDefaultInstance(new Properties()); MimeMessage message = new MimeMessage(session); message.setFrom(new InternetAddress("[email protected]")); message.setRecipient(Message.RecipientType.TO, new InternetAddress("[email protected]")); message.setSubject("测试"); //创建bodypart封装正文
        MimeBodyPart text = new MimeBodyPart(); text.setContent("这是中文邮件a<img src='cid:1.jpg'>", "text/html;charset=UTF-8"); //创建bodypart封装图片
        MimeBodyPart image = new MimeBodyPart(); image.setDataHandler(new DataHandler(new FileDataSource("src/1.jpg"))); image.setContentID("1.jpg"); //创建bodypart封装附件,解决乱码
        MimeBodyPart attach = new MimeBodyPart(); DataHandler dh = new DataHandler(new FileDataSource("src/光辉岁月.mp3")); attach.setDataHandler(dh); attach.setFileName(MimeUtility.encodeText(dh.getName()));//content-disposition //描述数据关系
        MimeMultipart content = new MimeMultipart(); content.addBodyPart(text); content.addBodyPart(image); content.setSubType("related"); 
     MimeBodyPart mbp
= new MimeBodyPart(); mbp.setContent(content); MimeMultipart mm = new MimeMultipart(); mm.addBodyPart(mbp); mm.addBodyPart(attach); mm.setSubType("mixed"); message.setContent(mm); message.saveChanges(); message.writeTo(new FileOutputStream("c:\\1.eml")); } }

ps:MIME协议已经突破原来的邮件领域广泛用在数据描述协议中,简单的一堆数据,不同类型,传给对方.

六、发送邮件

JavaMail API按其功能划分通常可分为如下三大类:
创建和解析邮件内容的API :Message类是创建和解析邮件的核心API,它的实例对象代表一封电子邮件.
发送邮件的API:Transport类是发送邮件的核心API类,它的实例对象代表实现了某个邮件发送协议的邮件发送对象,例如SMTP协议.
接收邮件的API:Store类是接收邮件的核心API类,它的实例对象代表实现了某个邮件接收协议的邮件接收对象,例如POP3协议.
Session类
Session类用于定义整个应用程序所需的环境信息,以及收集客户端与邮件服务器建立网络连接的会话信息,如邮件服务器的主机名、端口号、采用的邮件发送和接收协议等.Session对象根据这些信息构建用于邮件收发的Transport和Store对象,以及为客户端创建Message对象时提供信息支持.

使用JavaMail发送一封简单的邮件:

•创建包含邮件服务器的网络连接信息的Session对象.
•创建代表邮件内容的Message对象.
•创建Transport对象、连接服务器、发送Message、关闭连接.
//发送邮件
public class SendMail { public static void main(String[] args) throws Exception { Properties prop = new Properties(); prop.setProperty("mail.smtp.host", "localhost"); prop.setProperty("mail.transport.protocol", "smtp"); prop.setProperty("mail.smtp.auth", "true"); javax.mail.Session session = javax.mail.Session.getInstance(prop); session.setDebug(true);//打印与服务器的交互过程
 Message message = createMessage(session); Transport ts = session.getTransport(); ts.connect("aaa", "123"); ts.sendMessage(message,message.getAllRecipients());//获取收件人
 ts.close(); } private static Message createMessage(Session session) throws Exception { //创建邮件
        MimeMessage message = new MimeMessage(session); message.setFrom(new InternetAddress("[email protected]")); message.setRecipient(Message.RecipientType.TO, new InternetAddress("[email protected]")); message.setSubject("test"); message.setContent("aaaaaaaaaaaa", "text/html"); message.saveChanges(); return message; } }

七、WEB应用中集成邮件发送程序

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head><title>注册页面</title></head>
  
  <body>
    <form action="${pageContext.request.contextPath }/servlet/RegisterServlet" method="post"> 用户名:<input type="text" name="username"><br/> 密码:<input type="password" name="password"><br/> 电子邮箱:<input type="text" name="email"><br/>
        <input type="submit" value="注册">
    </form>
  </body>
</html>
//用户注册时,发送邮件通知
public class RegisterServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response)throws Exception { String username = request.getParameter("username"); String password = request.getParameter("password"); String email = request.getParameter("email"); User user = new User(); user.setEmail(email); user.setPassword(password); user.setUsername(username); try{ //开启发送邮件线程,如果不开线程的话,如邮件有问题,后续代码无法执行
            Thread t = new Thread(new SendMail(user)); t.start(); request.setAttribute("message", "注册成功!!"); }catch (Exception e) { e.printStackTrace(); request.setAttribute("message", "注册失败!!"); } request.getRequestDispatcher("/message.jsp").forward(request, response); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
public class SendMail implements Runnable { private  String host = "localhost"; private  String email = "[email protected]"; private  String username = "aaa"; private  String password = "123"; private User user; public SendMail(User user) { this.user = user; } public void run() { try{ Thread.sleep(1000*30); send(user); }catch (Exception e) { throw new RuntimeException(e); } } public void send(User user) throws Exception { Properties prop = new Properties(); prop.setProperty("mail.host", host); prop.setProperty("mail.transport.protocol", "smtp"); Session session = Session.getInstance(prop); Message message = createmessage(session, user); Transport ts = session.getTransport(); ts.connect(username, password); ts.sendMessage(message, message.getAllRecipients()); ts.close(); } public Message createmessage(Session session,User user) throws Exception{ MimeMessage message = new MimeMessage(session); message.setFrom(new InternetAddress(email)); message.setRecipient(Message.RecipientType.TO, new InternetAddress(user.getEmail())); message.setSubject("xxx网站用户注册邮件"); String content = "恭喜您,注册成功,您注册的用户名:"+ user.getUsername()!!"; message.setContent(content, "text/html;charset=UTF-8"); message.saveChanges(); return message; } }

八、邮件群发

1、邮件群发首先要采用递归查询的方式抓取邮箱,比如首先抓取新浪首页,抓到邮箱后保存起来,然后抓取到的url也保存起来(称为新浪的二级网址),再挨个访问url去抓取邮箱,依次类推,为了防止内存溢出,要进行层次控制,还要进行排重避免抓取相同的网址.
2、群发邮件的时候要进行多线程处理,加快速度,如10万-20万开启一个线程,20万-30万开启一个线程...
3、如我们使用新浪的邮箱进行群发,那么新浪会控制一个邮箱每天最多可以发多少封来控制群发,后来新浪发展到控制一个ip每天可以发多少封,可以使用adsl拨号(每次是一个ip)或者代理ip,但是都繁琐活不稳定,那么就需要自己搭建邮件服务器(在DNS注册,注册MX(为了能被别人找到)和A记录(为了和巨头邮件服务器连接时不需要账号密码)),因为我们的服务器还是要和其它巨头服务器相连,他们还是会禁掉我们的邮件服务器,他们巨头之间有邮件服务器白名单,一般小型公司很难加入,那么要实现群发,只能给巨头交钱.

你可能感兴趣的:(javaEE(17)_邮件原理与JavaMail开发)