一、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、电子邮件的传输过程



下图用于演示lisi@sina.com与wangwu@sohu.com帐户相互发送邮件的过程.



java 邮件json java邮件开发详解_java



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



准备实验环境



•提前在sohu和sina上注册一个免费帐号



•获取smtp和pop3服务器的名称



•Sina或sohu会在其帮助中心里提示管理当前注册帐号的邮件服务器的主机名.



•编写base64编码程序对用户名和密码进行编码.



•手工使用SMTP协议发送电子邮件



整个流程如下:



telnet localhost 25 //telnet 可连接互联网任意一台主机
ehlo flx
auth login
YWFh
MTIz
mail from: <aaa@flx.com>
rcpt to: <bbb@flx.com>

data
from: <jjs@taiwan.com>
to: <bbb@flx.com>
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: <aaa@flx.com>\r\n".getBytes());
        System.out.println(br.readLine());
        
        out.write("rcpt to: <bbb@flx.com>\r\n".getBytes());
        System.out.println(br.readLine());
        
        out.write("data\r\n".getBytes());
        System.out.println(br.readLine());
        
        out.write("from:<aaa@flx.com>\r\nto:<bbb@flx.com>\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("aaa@flx.com"));
        message.setRecipient(Message.RecipientType.TO, new InternetAddress("bbb@flx.com"));
        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("aaa@flx.com"));
        message.setRecipient(Message.RecipientType.TO, new InternetAddress("bbb@flx.com"));
        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("aaa@flx.com"));
        message.setRecipient(Message.RecipientType.TO, new InternetAddress("bbb@flx.com"));
        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("aaa@flx.com"));
        message.setRecipient(Message.RecipientType.TO, new InternetAddress("bbb@flx.com"));
        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协议已经突破原来的邮件领域广泛用在数据描述协议中,简单的一堆数据,不同类型,传给对方.

六、发送邮件

java 邮件json java邮件开发详解_java_02



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("aaa@flx.com"));
        message.setRecipient(Message.RecipientType.TO, new InternetAddress("bbb@flx.com"));
        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 = "aaa@flx.com";
    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记录(为了和巨头邮件服务器连接时不需要账号密码)),因为我们的服务器还是要和其它巨头服务器相连,他们还是会禁掉我们的邮件服务器,他们巨头之间有邮件服务器白名单,一般小型公司很难加入,那么要实现群发,只能给巨头交钱.