【Java】純底層SMTP實現郵件發送核心源碼__Java

來源:互聯網
上載者:User

 資訊來源: 軟體人才網    作者:林立超

       2002年我寫了一篇關於java實現郵件發送的文章,那時我寫的郵件發送功能很簡單,不能帶附件,不能帶模版。後來有很多朋友陸續問我,有沒有更完善一點的版本,現有我編寫的郵件發送程式設計思想是可以方便的發送附件,可以發送html頁麵包括內建圖片,樣式,並且新增了郵件中html元素背景圖片功能(這個功能是outlook沒有的),還有郵件串連池架構等等,下面我就把目前我使用很完善,功能也較強的版本核心源碼公布如下,希望能讓更多的技術人員掌握java郵件發送底層技術,有什麼問題可以在軟體人才網論壇發帖。

註:Base64為一種編碼方式,主要將位元據編碼為文本ascii編碼,利於文本傳輸。郵件體可以有多種編碼方法,base64是常用的一種。

package com.sunstudio.pool.mail;


import java.io.*;
import java.net.*;
import java.util.*;
import sun.misc.*;
import com.sigmaproject.config.*;
import com.sigmaproject.util.*;
import com.sigmaproject.stream.*;
import com.sunstudio.pool.*;


public class MailConnection extends PoolConnection{
 
 public static String CRLF=System.getProperty("line.separator");
 public static String CRLFTAB=CRLF+"/t";
 
 public static String[] TPL_START_FLAG=new String[]{
             " background=/"",
             " src=/"",
             " background='",
             " src='"
             };
 public static String[] TPL_END_FLAG=new String[]{
           "/"",
           "/"",
           "'",
           "'"
           };
 
 public static String TPL_INNER_NAME="f";
 
 public transient static final int LOW_PRIORITY  = 5;
 public transient static final int NORMAL_PRIORITY = 3;
 public transient static final int HIGH_PRIORITY  = 1;
 
 public static BASE64Encoder encoder=new BASE64Encoder();
 
 Socket socket=null;
 PureOutputStream outData=null;
 PureInputStream inData=null;
 boolean connected=false;
 boolean destoryed=false;
 boolean use_template=false;


 String smtpServer=null;
 String smtpPort=null;
 String fromUser=null;
 String pass=null;
 String fromName=null;
 boolean isNeedAuthLogin=false;
 boolean returnReceipt=false;
 String subject="";
 String textContent="";
 String htmlContent="";
 Hashtable tos=new Hashtable();
 Vector attach0=new Vector();
 Vector attach1=new Vector();
 int priority=3;
 
 public MailConnection(ConnectionPool pool1,String serverAddr,String serverPort,String user_email,String password){
  super(pool1,CONN_TYPE_MAIL);
  smtpServer=serverAddr;
  smtpPort=serverPort;
  user_email=(user_email==null||user_email.equals("")?"unknown":user_email);
  int s1=user_email.indexOf("<");
  if(s1==-1){
   fromUser=user_email;
   fromName=user_email;
  }else{
   fromUser=user_email.substring(s1+1,user_email.indexOf(">"));
   fromName=user_email.substring(0,s1);
  }
  pass=password;
 }
 /**使用模板發送郵件
  *
  * 注意:書寫模板內容時以及郵件體內容時要注意一定的格式,凡是郵件使用內建檔案時,需要特殊標註以便於組件進行替換和處理;
  * 使用郵件模板發送時可以另新增其他附件,但不用再指定郵件內容,即最好不要再調用setHtmlContent方法,否則innerattach將失效
  */
 public void useTemplate(String tpl,String source_path){
  attach0.removeAllElements();
  attach1.removeAllElements();
  tpl=tpl.toLowerCase();//mail中不能使用javascript基本,故可以做純html來操作
  Vector s1=new Vector(),d1=new Vector();
  int a=-1,b=-1,spos=0,x=0;
  String fa=null,fb=null,s=null,e=null,f=null;
  File ftmp=null;
  for(int i=0;i<TPL_START_FLAG.length;i++,spos=0){
   s=TPL_START_FLAG[i];
   e=TPL_END_FLAG[i];
   while(true){
    if((a=tpl.indexOf(s,spos))<0)break;//未找到起始標誌
    if((b=tpl.indexOf(e,a+s.length()))<0)break;//未找到結束標誌
    fa=tpl.substring(a+s.length(),b);
    spos=b+e.length();
    if(s1.contains((f=s+fa+e)))continue;
    if(!(ftmp=new File(source_path,fa)).exists())continue;
    fb=TPL_INNER_NAME+x++;
    s1.addElement(f);
    d1.addElement(s+"cid:"+fb+e);
    addInnerAttach(new Attachment(ftmp,fb));
    ftmp=null;
   }
  }
  setHtmlContent(StringEx.replace(tpl,s1,d1));
  use_template=true;
 }
 public void setReturnReceipt(boolean s){returnReceipt=s;}
 public void setPriority(int s){priority=s;}
 public void setTextContent(String s){if(s==null)return;textContent=s;}
 public void setHtmlContent(String s){if(s==null)return;htmlContent=s;}
 public void setSubject(String s){subject=(s==null||s.equals("")?" ":s);}
 public boolean addMailTo(String toName1,String toEmail){return tos.put(toEmail,toName1)!=null;}
 public boolean addMailTo(String user_email){
  int s1=user_email.indexOf("<");
  String name1="";
  String email1="";
  if(s1<0){
   name1=user_email;
   email1=user_email;
  }else{
   email1=user_email.substring(s1+1,user_email.indexOf(">"));
   name1=user_email.substring(0,s1);
  }
  return tos.put(email1,name1)!=null;
 }
 public void addInnerAttach(Attachment att){attach0.addElement(att);}
 public void addAttach(Attachment att){attach1.addElement(att);}
 public boolean connect(){
  try{
   fromName=(fromName==null?"":fromName);
   isNeedAuthLogin=(fromUser!=null&&pass!=null&&!fromUser.equals("")&&!pass.equals(""));
   socket=new Socket(smtpServer,Integer.parseInt(smtpPort));
   outData=new PureOutputStream(new BufferedOutputStream(socket.getOutputStream()));
   inData=new PureInputStream(socket.getInputStream());
   readResponse("220");
   sendRequestResponse("250","HELO "+smtpServer+CRLF);
   if(isNeedAuthLogin){
    sendRequestResponse("334","AUTH LOGIN"+CRLF);//AUTH LOGIN
    sendRequestResponse("334",new String(encoder.encode(fromUser.getBytes()))+CRLF);//USERNAME:
    sendRequestResponse("235",new String(encoder.encode(pass.getBytes()))+CRLF);//PASSWORD:
   }
   connected=true;
  }catch(Exception e){
   e.printStackTrace();
   connected=false;
   destory();
  }
  //System.out.println("郵件伺服器串連"+connected);
  return connected;
 }
 public boolean send(){
  try{
   //註:163郵箱不允許郵件人名稱和郵件人地址相同
   sendRequestResponse("250","RSET"+CRLF);
   sendRequestResponse("250","MAIL FROM: "+"<"+fromUser+">"+CRLF);//命令不需要帶郵件人名稱,切記!!!!!
   for(Enumeration enu=tos.keys();enu.hasMoreElements();){
    Object to1=enu.nextElement();
    if(to1==null)continue;
    String touser=(String)to1;
    String toname=(String)tos.get(to1);
    sendRequestResponse("250,550","RCPT TO: "+"<"+touser+">"+CRLF);//命令不需要帶郵件人名稱,切記!!!!!
   }
   sendRequestResponse("354","DATA"+CRLF);
   if(!sendBody())throw new Exception("發送郵件內容時出錯!");
   sendRequestResponse("250",CRLF+"."+CRLF);
   return true;
  }catch(Exception e){
   e.printStackTrace();
   destory();
  }
  return false;
 }
 public boolean isAvailable(){
  while(!connected){
   connect();
   try{Thread.currentThread().sleep(100);}catch(Exception e){}
  }
  return socket!=null
      &&connected
      &&!destoryed
      &&!socket.isClosed()
      &&!socket.isOutputShutdown()
      &&!socket.isInputShutdown();
 }
 public void close(){
  try{
   returnReceipt=false;
   subject="";
   textContent="";
   htmlContent="";
   tos.clear();
   attach0.clear();
   attach1.clear();
   sendRequestResponse("250","RSET"+CRLF);
  }catch(Exception e){
   destory();
  }
 }
 public void destory(){
  try{
   sendRequestResponse("221","QUIT"+CRLF);//QUIT退出
  }catch(Exception e){} 
  try{
   closeSocket();
  }catch(Exception e){} 
  destoryed=true;
 }
 public boolean isDestory(){
  return destoryed ;
 }
 private void closeSocket(){
  try{if(inData!=null){inData.close();inData=null;}}catch(Exception ex){}
  try{if(outData!=null){outData.close();outData=null;}}catch(Exception ex){}
  try{if(socket!=null){socket.close();socket=null;}}catch(Exception ex){}
 }
 private String getGBKEncodeStr(String x){
  return "=?gb2312?B?"+Base64.encodeString(x==null?"":x)+"?=";
 }
 private void readResponse(String cmd)throws Exception{
  String tmp=inData.readLine();
  String code=tmp.substring(0,3);
  //System.out.println("S:"+tmp);
  if((","+cmd+",").indexOf(code)>-1);
  else throw new Exception("##########郵件發送失敗!##########"+tmp+"("+cmd+")");
  while(tmp.startsWith(cmd+"-"))tmp=inData.readLine();
 }
 private void sendRequest(String msg)throws Exception{
  //System.out.println("C:"+msg);
  outData.print(msg);
  outData.flush();
 }
 private void sendRequestResponse(String cmd,String msg)throws Exception{
  sendRequest(msg);
  readResponse(cmd);
 }
 private boolean sendBody(){
  String boundary0="----=hawa_multipart_mixed_"+MD5.encode(String.valueOf(Math.random()+1));
  String boundary1="----=hawa_multipart_related_"+MD5.encode(String.valueOf(Math.random()+2));
  String boundary2="----=hawa_multipart_alternative_"+MD5.encode(String.valueOf(Math.random()+3));
  try{
   //郵件標頭資訊區////////////////////////////
   outData.print("Date: "+new Date().toGMTString()+CRLF);
   outData.print("From: "+"/""+getGBKEncodeStr(fromName)+"/" <"+fromUser+">"+CRLF);
   outData.print("To: ");
   int tmp=0;
   String to1=null;
   for(Enumeration enu=tos.keys();enu.hasMoreElements();){
    to1=(String)enu.nextElement();
    outData.print((tmp==0?"":","+CRLFTAB)+"/""+getGBKEncodeStr((String)tos.get(to1))+"/" <"+to1+">");
    tmp++;
   }
   outData.print(CRLF);
   outData.print("Subject: ");
   for(int i=0;i<subject.length();i+=26){
    if(i>0)outData.print(CRLFTAB);
    outData.print(getGBKEncodeStr(subject.substring(i,Math.min(subject.length(),i+26))));
   }
   outData.print(CRLF);
  
   outData.print("Message-ID: <"+System.currentTimeMillis()+fromUser.substring(fromUser.indexOf("@"))+">"+CRLF);
   outData.print("X-Priority: "+priority+CRLF);
   outData.print("X-Mailer: Hawa Java Mailer"+CRLF);
   //閱讀回執
   if(returnReceipt)outData.print("Disposition-Notification-To: "+"/""+getGBKEncodeStr(fromName)+"/" <"+fromUser+">"+CRLF);
   outData.print("MIME-Version: 1.0"+CRLF);
   outData.print("Content-Type: multipart/mixed;boundary=/""+boundary0+"/""+CRLF);
   outData.print(CRLF);
   ///郵件體資料區(分隔字元定義)///////////////////////////////////////////////////////////////////
   outData.print("This is a multi-part message in MIME format."+CRLF);
   outData.print("--"+boundary0+CRLF);
   outData.print("Content-Type: multipart/related;type=/"multipart/alternative/";"+CRLFTAB+"boundary=/""+boundary1+"/""+CRLF);
   outData.print(CRLF);
    outData.print("//Alternative boundaryID"+CRLF);
    outData.print("--"+boundary1+CRLF);
    outData.print("Content-Type: multipart/alternative;"+CRLFTAB+"boundary=/""+boundary2+"/""+CRLF);
    outData.print(CRLF);
    ///郵件普通文本///////////////////////////////////////////////////////
     outData.print("//Alternative plaintext"+CRLF);
     outData.print("--"+boundary2+CRLF);
     outData.print("Content-Type: text/plain;"+CRLFTAB+"charset=/"gb2312/";"+CRLF);
     outData.print("Content-Transfer-Encoding: base64"+CRLF);
     outData.print("Content-Disposition: inline"+CRLF);
     outData.print(CRLF);
     outData.print(encoder.encode(textContent.getBytes())+CRLF);
      outData.print(CRLF);
     ///郵件HTML文本///////////////////////////////////////////////////////
     outData.print("//Alternative html"+CRLF);
     outData.print("--"+boundary2+CRLF);
     outData.print("Content-Type: text/html;"+CRLFTAB+"charset=/"GB2312/""+CRLF);
     outData.print("Content-Transfer-Encoding: base64"+CRLF);
     outData.print("Content-Disposition: inline"+CRLF);
     outData.print(CRLF);
     outData.print(encoder.encode(htmlContent.getBytes())+CRLF);
     outData.print(CRLF);
     outData.print("--"+boundary2+"--"+CRLF);
     outData.print(CRLF);
    ///郵件HTML連結Content-ID(主要包含image和style)///////////////////////////////////////////////////////
    for(Enumeration enu=attach0.elements();enu.hasMoreElements();)((Attachment)enu.nextElement()).writeToMail(outData,boundary1);
    outData.print("--"+boundary1+"--"+CRLF);
   outData.print(CRLF);
   ///郵件附件///////////////////////////////////////////////////////
   for(Enumeration enu=attach1.elements();enu.hasMoreElements();)((Attachment)enu.nextElement()).writeToMail(outData,boundary0);
   outData.print("--"+boundary0+"--"+CRLF);
  //////////////////////////////////////////////////////////
   outData.flush();
  }catch(Exception e){
   return (connected=false);
  }
  return true;
 }
 public static String GetMIMEType(String filename){
  if(filename==null||filename.equals("")||filename.indexOf(".")==-1)return("application/octet-stream");
  String ext=filename.substring(filename.lastIndexOf(".")).toLowerCase();
  String ret=SystemConfig.getParameter(ext);
  if(ret==null||ret.equals(""))return("application/octet-stream");
  return ret;
 }
}

                                        軟體人才網http://www.soft-hr.cn歡迎您。
 
 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.