Java程式Mail、EDM(電子型錄)寄送

這個範例主要的概念是抓取網路上EDM的html內容,讀取外部的寄送email清單文字檔,依續一個個email寄送出EDM內容,寄送的每一封信件都使用thread來獨立完成,而且寄送中間都有固定的間隔時間。

JavaMail是Java用來處理Mail事件的一個Framework,提供寄信SMTP、收信POP3等常用到的Mail Protocol,主要由sun(目前為oracle)來維護,是一個官方的套件,可由此下載

發送Mail的主體程式Thread

Mail發送的內容為html,所以需要使用Multipart來承載內容,如果是純文字則可直接使用Message的setContent來加入信件內容。

package com.izero.mail.thread;

import java.util.*;

import javax.mail.*;

import javax.mail.internet.*;

public class Sender extends java.lang.Thread {
    private String content;
    // log
    private org.apache.log4j.Logger log;
    // 郵件標題
    private String subject;
    // 寄件對向
    private String to;
    // 設定檔
    private Properties p;

    // 建構
    public Sender(String to, String subject, String content, Properties p) {
        super();
        this.content = content;
        this.to = to;
        log = org.apache.log4j.Logger.getLogger(this.getClass());
        this.p = p;
        this.subject = subject;
    }

    // thread run
    public void run() {
        try {

            // 設定傳送基本資訊
            // 取得mail host名稱
            String host = p.getProperty("host");
            // 取得寄信者mail
            String from = p.getProperty("from");

            // 取得寄信者帳號
            String user = p.getProperty("user");
            // 取得寄信者帳號密碼
            String pwd = p.getProperty("pwd");

            Properties props = System.getProperties();

            // 設定SMTP server

            props.put("mail.smtp.host", host);
            // 是否需要認證
            props.put("mail.smtp.auth", "true");

            // 依據Properties建立一個Session

            Session mailSession = Session.getInstance(props, null);

            // 從Session建立一個Message
            MimeMessage message = new MimeMessage(mailSession);

            // 設定mail From
            message.setFrom(new InternetAddress(from, p.getProperty("fromName",
                    "utf-8")));
            // 設定mail To
            message.addRecipient(Message.RecipientType.TO, new InternetAddress(
                    to));
            // 設定標題
            message.setSubject(this.subject);

            // 設定寄件日期
            message.setSentDate(new Date());
            // 建立郵件本文內容
            Multipart multipart = new MimeMultipart();

            // 郵件內容
            BodyPart contentPart = new MimeBodyPart();
            // 給BodyPart對像設置內容和格式/編碼方式
            contentPart.setContent(content, "text/html;charset=utf8");

            // 加入郵件內容到mutipart裡
            multipart.addBodyPart(contentPart);

            // 將multipart加到mail的message裡
            message.setContent(multipart);
            // 產生mail message
            message.saveChanges();

            Transport transport = mailSession.getTransport("smtp");
            // 傳送
            transport.connect(host, user, pwd);
            transport.sendMessage(message, message.getAllRecipients());
            transport.close();
            log.info(to + ",寄送完成");

        } catch (Exception e) {

            log.info(this.to + "-" + e.getMessage());
            e.printStackTrace();

        }

    }
}

取得網頁內容程式WebModule

請參考java HttpURLConnection來實作get及post動作

package com.izero.http;

import java.net.*;
import java.io.*;

public class WebModule {
    private org.apache.log4j.Logger logger;
    private StringBuffer buff;

    public WebModule() {
        logger = org.apache.log4j.Logger.getLogger(this.getClass());
        buff = new StringBuffer();
    }

    public boolean doPost(String sURL, String data, String cookie,
            String referer, String charset) {

        boolean doSuccess = true;
        java.io.BufferedWriter wr = null;
        try {

            URL url = new URL(sURL);
            HttpURLConnection URLConn = (HttpURLConnection) url
                    .openConnection();

            URLConn.setDoOutput(true);
            URLConn.setDoInput(true);
            ((HttpURLConnection) URLConn).setRequestMethod("POST");
            URLConn.setUseCaches(false);
            URLConn.setAllowUserInteraction(true);
            HttpURLConnection.setFollowRedirects(true);
            URLConn.setInstanceFollowRedirects(true);

            URLConn.setRequestProperty(
                    "User-agent",
                    "Mozilla/5.0 (Windows; U; Windows NT 6.0; zh-TW; rv:1.9.1.2) "
                            + "Gecko/20090729 Firefox/3.5.2 GTB5 (.NET CLR 3.5.30729)");
            URLConn.setRequestProperty("Accept",
                    "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
            URLConn.setRequestProperty("Accept-Language",
                    "zh-tw,en-us;q=0.7,en;q=0.3");
            URLConn.setRequestProperty("Accept-Charse",
                    "Big5,utf-8;q=0.7,*;q=0.7");
            if (cookie != null)
                URLConn.setRequestProperty("Cookie", cookie);
            if (referer != null)
                URLConn.setRequestProperty("Referer", referer);

            URLConn.setRequestProperty("Content-Type",
                    "application/x-www-form-urlencoded");
            URLConn.setRequestProperty("Content-Length",
                    String.valueOf(data.getBytes().length));

            java.io.DataOutputStream dos = new java.io.DataOutputStream(
                    URLConn.getOutputStream());
            dos.writeBytes(data);

            java.io.BufferedReader rd = new java.io.BufferedReader(
                    new java.io.InputStreamReader(URLConn.getInputStream(),
                            charset));
            String line;
            while ((line = rd.readLine()) != null) {
                buff.append(line);
            }

            rd.close();
        } catch (java.io.IOException e) {
            doSuccess = false;
            logger.info(e);

        } finally {
            if (wr != null) {
                try {
                    wr.close();
                } catch (java.io.IOException ex) {
                    logger.info(ex);
                }
                wr = null;
            }
        }

        return doSuccess;
    }

    public boolean doGet(String sURL, String cookie, String referer,
            String charset) {
        boolean doSuccess = true;
        BufferedReader in = null;
        try {
            URL url = new URL(sURL);
            HttpURLConnection URLConn = (HttpURLConnection) url
                    .openConnection();
            URLConn.setRequestProperty(
                    "User-agent",
                    "Mozilla/5.0 (Windows; U; Windows NT 6.0; zh-TW; rv:1.9.1.2) "
                            + "Gecko/20090729 Firefox/3.5.2 GTB5 (.NET CLR 3.5.30729)");
            URLConn.setRequestProperty("Accept",
                    "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
            URLConn.setRequestProperty("Accept-Language",
                    "zh-tw,en-us;q=0.7,en;q=0.3");
            URLConn.setRequestProperty("Accept-Charse",
                    "Big5,utf-8;q=0.7,*;q=0.7");

            if (cookie != null)
                URLConn.setRequestProperty("Cookie", cookie);
            if (referer != null)
                URLConn.setRequestProperty("Referer", referer);
            URLConn.setDoInput(true);
            URLConn.setDoOutput(true);
            URLConn.connect();
            URLConn.getOutputStream().flush();
            in = new BufferedReader(new InputStreamReader(
                    URLConn.getInputStream(), charset));

            String line;
            while ((line = in.readLine()) != null) {
                buff.append(line);
            }

        } catch (IOException e) {
            doSuccess = false;
            logger.info(e);
            e.printStackTrace();
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (java.io.IOException ex) {
                    logger.info(ex);
                }
                in = null;

            }
        }

        return doSuccess;
    }

    public String getContent() {
        return buff.toString();
    }
}

設定檔內容

host=你的mail主機
from=你的mail
auth=true
user=你的帳號
pwd=你的密碼
sleep=5000
fromName=寄信者顯示名稱
edmList=d:/mail/list.txt
edmUrl=edm的網址
edmSubject=郵件標題

主程式

也是個thread,讀取外部email清單,取得edm的html內容,配製寄送的thread。

mail清單列表需要是一個文字檔,每一個mail一行。

package com.izero.mail;

import java.util.Properties;

public class Main extends java.lang.Thread {
    private org.apache.log4j.Logger log;
    private java.util.ArrayList<String> toList;

    private Properties p;
    private String content;
    private String subject;

    public Main() throws java.io.IOException {
        super();

        log = org.apache.log4j.Logger.getLogger(this.getClass());
        java.io.FileReader fis = null;
        java.io.BufferedReader bis = null;
        toList = new java.util.ArrayList<String>();

        try {
            // 從外部取得設定檔資料
            p = new Properties();
            log.info(this.getClass().getResource("/mail.properties").getPath());
            java.io.FileReader fr = new java.io.FileReader(this.getClass()
                    .getResource("/mail.properties").getPath());
            java.io.BufferedReader br = new java.io.BufferedReader(fr);
            String str;
            while ((str = br.readLine()) != null) {
                String[] tmp = str.split("=");
                p.setProperty(tmp[0], tmp[1]);
            }
            br.close();
            fr.close();

            // 名單
            fis = new java.io.FileReader(p.getProperty("edmList"));
            bis = new java.io.BufferedReader(fis);

            while ((str = bis.readLine()) != null) {
                toList.add(str.trim());
            }
            fis.close();
            bis.close();
            fis = null;
            bis = null;

            // 取得網頁內容
            com.izero.http.WebModule web = new com.izero.http.WebModule();
            if (web.doGet(p.getProperty("edmUrl"), null, null, "utf-8")) {
                content = web.getContent();
            } else {
                throw new java.io.IOException("取得網頁資料錯誤!");
            }

            // 郵件標題
            this.subject = p.getProperty("edmSubject");

        } catch (java.io.IOException e) {
            log.info(e.getMessage());

            throw e;
        } finally {
            try {
                if (bis != null)
                    bis.close();

                if (fis != null)
                    fis.close();
                bis = null;
                fis = null;
            } catch (java.io.IOException ex) {

            }
        }

    }

    public void run() {

        java.io.FileWriter fw = null;

        try {

            for (int i = 0; i < toList.size(); i++) {

                // 啟動thread開始寄信
                com.izero.mail.thread.Sender sender = new com.izero.mail.thread.Sender(
                        toList.get(i), subject, content, p);
                sender.start();
                // 暫停數秒後再開始寄信
                Thread.sleep(Integer.parseInt(p.getProperty("sleep")));

                if (fw != null) {
                    fw.flush();
                    fw.write(toList.get(i) + "\r\n");
                }

            }

        } catch (Exception e) {
            log.info(e.getMessage());
            e.printStackTrace();
        } finally {
            if (fw != null) {
                try {
                    fw.flush();
                    fw.close();
                } catch (Exception e) {
                }

            }
        }

    }

    public static void main(String args[]) throws java.io.IOException {
        Main m = new Main();
        m.start();
    }
}

log4j的設定檔

log4j.rootLogger=INFO,R, stdout
#log4j.logger.system = debug,R


log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p    %c- %m%n

log4j.appender.R=org.apache.log4j.DailyRollingFileAppender
log4j.appender.R.Append=true
log4j.appender.R.File.DatePattern='.'yyyy-MM-dd
log4j.appender.R.File=mail.log


#log4j.appender.R.MaxFileSize=100KB
#log4j.appender.R.MaxBackupIndex=5

log4j.appender.R.layout=org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern=%d %p    %c- %m%n

11 thoughts to “Java程式Mail、EDM(電子型錄)寄送”

    1. 不好意思!程式不知道丟那裡去了
      你可以直接copy畫面上的下來,我全都有提供了

  1. 請問ㄧ下設定檔內容
    host=你的mail主機
    from=你的mail
    auth=true
    user=你的帳號
    pwd=你的密碼
    sleep=5000
    fromName=寄信者顯示名稱
    edmList=d:/mail/list.txt
    edmUrl=edm的網址
    edmSubject=郵件標題

    這些是要寫在哪裡???在上面的JAVA程式直接設定嗎??
    上面都是JAVA檔
    那JSP 要寫什麼??
    例如:

    之類的

    初學者請多包涵
    謝謝

    1. 那是設定檔

      java.io.FileReader fr = new java.io.FileReader(this.getClass()
                          .getResource("/mail.properties").getPath());
      

      會去讀mail.properties這個檔案,而內容就是你所提到的那些
      jsp要寫什麼….就看public static void main 那裡怎麼呼叫
      jsp就用那種方法呼叫就可以了

  2. 想請問一下,收件者跟收件內容來源都是DB 要如何修正,這樣是會使用多執行緒去跑嗎?

    1. 你可以把這一段改成你想要的部份

      // 從外部取得設定檔資料
                  p = new Properties();
                  log.info(this.getClass().getResource("/mail.properties").getPath());
                  java.io.FileReader fr = new java.io.FileReader(this.getClass()
                          .getResource("/mail.properties").getPath());
                  java.io.BufferedReader br = new java.io.BufferedReader(fr);
                  String str;
                  while ((str = br.readLine()) != null) {
                      String[] tmp = str.split("=");
                      p.setProperty(tmp[0], tmp[1]);
                  }
                  br.close();
                  fr.close();
      
                  // 名單
                  fis = new java.io.FileReader(p.getProperty("edmList"));
                  bis = new java.io.BufferedReader(fis);
      
                  while ((str = bis.readLine()) != null) {
                      toList.add(str.trim());
                  }
                  fis.close();
                  bis.close();
                  fis = null;
                  bis = null;
      
  3. TKS,已經OK 了,不過現在還到一個小問題..之前設 message.setFrom(new InternetAddress(from,fromName ),fromNname 會顯示不出來,收到mail 時還是寄件者的mailaddress 但寄信者顯示名稱卻沒有顯示,但在setFrom 前列印出來是有值的….

    1. 有可能是這個寄件者的聯絡人己經被你加入你的郵件接收軟體的清單裡了,而名字是用mail address,所以才顯示不出來,您可以先檢查看看…如果不行的話再告知,我再來測一下

  4. @yku,
    new InternetAddress(from,fromDisplayName,”utf-8″); 這樣就OK 了..
    現在還到一個比較棘手的問題..因為要記錄每一個人點EDM 裡的每一個連結,需要大量置換edm 內容的某些字串,但會出現Exception in thread “main” java.lang.OutOfMemoryError: Java heap space..不知大大有沒有什麼好方法可以解?TKS

    1. 其實這outOfMemory可能會在三個地方產生
      一個是在你使用資料庫連線時,用完後(或大量使用)沒有關閉,
      每一個Connection在一般預設可能要三十分鐘才會被強迫關閉,
      這很容易就會OutOfMemory了

      另一種是在控制Collection元件時發生,像是ArrayList或是ResultSet,
      如果讓它們一次載入太多資料時,因為需要暫存等待被使用,
      所以很容易吃很重的記憶體,建議如果是ResultSet的話,
      一次查詢少量,用完close及=null後再重查取得資料

      還有一種是你的Thread大量使用後,並沒有確實讓每一個Thread都跑完,
      每個Thread不管有任何狀況,一定要確實讓它可以run到結束,
      除非是特殊用途

      這二篇是以前遇到時解決的方法,您可以參考看看
      http://blog.yslifes.com/archives/360
      http://blog.yslifes.com/archives/362

      這是我大概有碰到的狀況,如果還是無法接解您的狀況,您可以mail給我你的原始碼,讓我幫忙查一下^^

發表迴響