Gmail, Authentication in JavaMail
JavaMail_Intro_송수신_html_첨부.ppt
JavaMail 1.4.3:http://java.sun.com/products/javamail/downloads/index.html
JavaMail 1.4.4 http://www.oracle.com/technetwork/java/index-138643.html
javamail1_4_4.zip
JAF 1.1.1: http://java.sun.com/products/javabeans/jaf/downloads/index.html
JavaMail 1.5
http://www.oracle.com/technetwork/java/javamail/javamail145-1904579.html
JAF는 Java SE 6이상의 버전을 사용할 경우 이미 포함되어 있으므로 다운로드할 필요가 없다.
웹프로젝트에서 Tomcat과 JavaMail API를 사용하는 경우에 간혹 Tomcat에서 javax.mail.Authenticator 클래스를 찾지 못하는 현상이 있을 수 있는데, 이때는 mail.jar, activation.jar 를 Tomcat/common/lib/ 안에 복사해 주면 된다.
Gmail 에서 제공하는 SMTP 사용을 위한 Gmail 계정 설정 참고 http://micropilot.tistory.com/category/Java%20Mail/Gmail%20Account
Gmail.java
import javax.mail.*;
import javax.mail.internet.*;
import javax.activation.*;
import java.io.*;
import java.util.*;
import java.security.Security;
public class Gmail
{
public static void main(String[] args)
{
Properties p = new Properties();
p.put("mail.smtp.user", "gmail_id@gmail.com"); // Google계정@gmail.com으로 설정
p.put("mail.smtp.host", "smtp.gmail.com");
p.put("mail.smtp.port", "465");
p.put("mail.smtp.starttls.enable","true");
p.put( "mail.smtp.auth", "true");
p.put("mail.smtp.debug", "true");
p.put("mail.smtp.socketFactory.port", "465");
p.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
p.put("mail.smtp.socketFactory.fallback", "false");
//Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
try {
Authenticator auth = new SMTPAuthenticator();
Session session = Session.getInstance(p, auth);
session.setDebug(true); // 메일을 전송할 때 상세한 상황을 콘솔에 출력한다.
//session = Session.getDefaultInstance(p);
MimeMessage msg = new MimeMessage(session);
String message = "Gmail SMTP 서버를 이용한 JavaMail 테스트";
msg.setSubject("Gmail SMTP 서버를 이용한 JavaMail 테스트");
Address fromAddr = new InternetAddress("gmail_id@gmail.com"); // 보내는 사람의 메일주소
msg.setFrom(fromAddr);
Address toAddr = new InternetAddress("paran_id@paran.com"); // 받는 사람의 메일주소
msg.addRecipient(Message.RecipientType.TO, toAddr);
msg.setContent(message, "text/plain;charset=KSC5601");
System.out.println("Message: " + msg.getContent());
Transport.send(msg);
System.out.println("Gmail SMTP서버를 이용한 메일보내기 성공");
}
catch (Exception mex) { // Prints all nested (chained) exceptions as well
System.out.println("I am here??? ");
mex.printStackTrace();
}
}
private static class SMTPAuthenticator extends javax.mail.Authenticator {
public PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication("gmail_id", "gmail_pwd"); // Google id, pwd, 주의) @gmail.com 은 제외하세요
}
}
}
GoogleTest.java(다수에게 메일을 전송하는 경우)
import java.security.Security;
import java.util.Properties;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
public class GoogleTest {
private static final String SMTP_HOST_NAME = "smtp.gmail.com";
private static final String SMTP_PORT = "465";
private static final String emailMsgTxt = "Gmail SMTP 서버를 사용한 JavaMail 테스트";
private static final String emailSubjectTxt = "Gmail SMTP 테스트";
private static final String emailFromAddress = "cwisky@yahoo.com";
private static final String SSL_FACTORY = "javax.net.ssl.SSLSocketFactory";
private static final String[] sendTo = { "cwisky@navy.com"}; // 수신자가 다수일 경우를 가정해서 배열을 사용함
public static void main(String args[]) throws Exception {
Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
new GoogleTest().sendSSLMessage(sendTo, emailSubjectTxt, emailMsgTxt, emailFromAddress);
System.out.println("Sucessfully Sent mail to All Users");
}
public void sendSSLMessage(String recipients[], String subject,
String message, String from) throws MessagingException {
boolean debug = true;
Properties props = new Properties();
props.put("mail.smtp.host", SMTP_HOST_NAME);
props.put("mail.smtp.auth", "true");
props.put("mail.debug", "true");
props.put("mail.smtp.port", SMTP_PORT);
props.put("mail.smtp.socketFactory.port", SMTP_PORT);
props.put("mail.smtp.socketFactory.class", SSL_FACTORY);
props.put("mail.smtp.socketFactory.fallback", "false");
Session session = Session.getDefaultInstance(props,
new javax.mail.Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication("gmail_id", "gmail_pwd");
}
}
);
session.setDebug(debug);
Message msg = new MimeMessage(session);
InternetAddress addressFrom = new InternetAddress(from);
msg.setFrom(addressFrom);
InternetAddress[] addressTo = new InternetAddress[recipients.length];
for (int i = 0; i < recipients.length; i++) {
addressTo[i] = new InternetAddress(recipients[i]);
}
msg.setRecipients(Message.RecipientType.TO, addressTo);
// Setting the Subject and Content Type
msg.setSubject(subject);
msg.setContent(message, "text/plain;charset=KSC5601");
Transport.send(msg);
}
}
GMail SMTP 서버를 이용하여 텍스트와 첨부파일을 전송하는 예제 프로그램
package mail;
import java.io.File;
import java.security.Security;
import java.util.Properties;
import javax.activation.DataHandler;
import javax.activation.FileDataSource;
import javax.mail.BodyPart;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
public class GmailSMTP {
private static final String SMTP_HOST_NAME = "smtp.gmail.com";
private static final String SMTP_PORT = "465";
private static final String emailMsgTxt = "Gmail SMTP 서버를 사용한 JavaMail 테스트";
private static final String emailSubjectTxt = "Gmail SMTP 테스트";
private static final String emailFromAddress = "google_id@gmail.com";
private static final String SSL_FACTORY = "javax.net.ssl.SSLSocketFactory";
private static final String[] sendTo = { "paran_id@paran.com"};
public static void main(String args[]) throws Exception {
Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
new GmailSMTP().sendSSLMessage(sendTo, emailSubjectTxt, emailMsgTxt, emailFromAddress);
System.out.println("Sucessfully Sent mail to All Users");
}
public void sendSSLMessage(String recipients[], String subject,
String message, String from) throws MessagingException {
boolean debug = true;
Properties props = new Properties();
props.put("mail.smtp.host", SMTP_HOST_NAME);
props.put("mail.smtp.auth", "true");
props.put("mail.debug", "true");
props.put("mail.smtp.port", SMTP_PORT);
props.put("mail.smtp.socketFactory.port", SMTP_PORT);
props.put("mail.smtp.socketFactory.class", SSL_FACTORY);
props.put("mail.smtp.socketFactory.fallback", "false");
Session session = Session.getDefaultInstance(props,
new javax.mail.Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication("gmail_id", "gmail_pwd");
}
}
);
session.setDebug(debug);
Message msg = new MimeMessage(session);
InternetAddress addressFrom = new InternetAddress(from);
msg.setFrom(addressFrom);
InternetAddress[] addressTo = new InternetAddress[recipients.length];
for (int i = 0; i < recipients.length; i++) {
addressTo[i] = new InternetAddress(recipients[i]);
}
msg.setRecipients(Message.RecipientType.TO, addressTo);
// Setting the Subject and Content Type
msg.setSubject(subject);
/*텍스트만 전송하는 경우 아래의 2라인만 추가하면 된다.
* 그러나 텍스트와 첨부파일을 함께 전송하는 경우에는 아래의 2라인을 제거하고
* 대신에 그 아래의 모든 문장을 추가해야 한다.
**/
//msg.setContent(message, "text/plain;charset=KSC5601");
//Transport.send(msg);
/* 텍스트와 첨부파일을 함께 전송하는 경우에는 위의 2라인을 제거하고 아래의 모든 라인을 추가한다.*/
// Create the message part
BodyPart messageBodyPart = new MimeBodyPart();
// Fill the message
messageBodyPart.setText("테스트용 메일의 내용입니다.");
Multipart multipart = new MimeMultipart();
multipart.addBodyPart(messageBodyPart);
// Part two is attachment
messageBodyPart = new MimeBodyPart();
File file = new File("C:/append.txt");
FileDataSource fds = new FileDataSource(file);
messageBodyPart.setDataHandler(new DataHandler(fds));
String fileName = fds.getName(); // 한글파일명은 영문으로 인코딩해야 첨부가 된다.
fileName = new String(fileName.getBytes("KSC5601"), "8859_1");
messageBodyPart.setFileName(fileName);
multipart.addBodyPart(messageBodyPart);
// Put parts in message
msg.setContent(multipart);
// Send the message
Transport.send(msg);
System.out.println("E-mail successfully sent!!");
}
}
위의 내용으로 오류가 발생한다면 아래처럼......
package mail;
import javax.mail.*;
import javax.mail.internet.*;
import javax.activation.*;
import java.io.*;
import java.util.*;
import java.security.Security;
public class GMail
{
public static void main(String[] args)
{
Properties p = new Properties();
p.put("mail.smtp.user", "myid@gmail.com"); // Google계정 아이디@gmail.com으로 설정
p.put("mail.smtp.host", "smtp.gmail.com");
p.put("mail.smtp.port", "465");
p.put("mail.smtp.starttls.enable","true");
p.put( "mail.smtp.auth", "true");
p.put("mail.smtp.debug", "true");
p.put("mail.smtp.socketFactory.port", "465");
p.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
p.put("mail.smtp.socketFactory.fallback", "false");
//Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
try {
Authenticator auth = new SMTPAuthenticator();
Session session = Session.getInstance(p, auth);
session.setDebug(true); // 메일을 전송할 때 상세한 상황을 콘솔에 출력한다.
//session = Session.getDefaultInstance(p);
MimeMessage msg = new MimeMessage(session);
String message = "Gmail SMTP 서버를 이용한 JavaMail 테스트";
msg.setSubject("Gmail SMTP 서버를 이용한 JavaMail 테스트");
Address fromAddr = new InternetAddress("google아이디@gmail.com"); // 보내는 사람의 메일주소
msg.setFrom(fromAddr);
Address toAddr = new InternetAddress("수신자아이디@paran.com"); // 받는 사람의 메일주소
msg.addRecipient(Message.RecipientType.TO, toAddr);
/*
msg.setContent(message, "text/plain;charset=KSC5601");
System.out.println("Message: " + msg.getContent());
Transport.send(msg);
*/
BodyPart messageBodyPart = new MimeBodyPart();
// Fill the message
messageBodyPart.setText("Java Mail API를 이용하여 첨부파일을 테스트합니다.");
Multipart multipart = new MimeMultipart();
multipart.addBodyPart(messageBodyPart);
// Part two is attachment
messageBodyPart = new MimeBodyPart();
File file = new File("C:/log.txt");
FileDataSource fds = new FileDataSource(file);
messageBodyPart.setDataHandler(new DataHandler(fds));
messageBodyPart.setFileName(fds.getName());
multipart.addBodyPart(messageBodyPart);
// Put parts in message
msg.setContent(multipart, "text/plain;charset=KSC5601");
// Send the message
Transport.send(msg);
System.out.println("Gmail SMTP서버를 이용한 메일보내기 성공");
}
catch (Exception mex) { // Prints all nested (chained) exceptions as well
System.out.println("I am here??? ");
mex.printStackTrace();
}
}
private static class SMTPAuthenticator extends javax.mail.Authenticator {
public PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication("구글아이디", "구글암호"); // Google id, pwd
}
}
}
Gmail_POP3.java (Gmail계정에 POP3프로토콜을 사용하여 접속하고 자신의 계정에 저장되어 있는 메일을 가져오는 예)
package mail;
public class GMailPOP3 {
static final String USERNAME = "gmail_id@gmail.com";
static final String PASSWORD = "00000000";
public GMailPOP3() {}
public static void main(String[] args) {
try {
GMailUtilities gmail = new GMailUtilities();
gmail.setUserPass(USERNAME, PASSWORD); // Gmail 계정 메일주소, 암호
gmail.connect();
gmail.openFolder("INBOX");
int totalMessages = gmail.getMessageCount();
int newMessages = gmail.getNewMessageCount();
System.out.println("Total messages = " + totalMessages);
System.out.println("New messages = " + newMessages);
System.out.println("-------------------------------");
//Uncomment the below line to print the body of the message.
//Remember it will eat-up your bandwidth if you have 100's of messages.
//gmail.printAllMessageEnvelopes();
//gmail.printAllMessages();
POP3DTO[] dtos = gmail.getAllMessages();
for(int i=0;i<dtos.length;i++){
pr(dtos[i].getFrom().toString());
pr(dtos[i].getSubject());
pr(dtos[i].getSentDate().toString());
}
} catch(Exception e) {
e.printStackTrace();
System.exit(-1);
}
}
private static void pr(String str){
System.out.println(str);
}
}
GmailUtilities.java
package mail;
import com.sun.mail.pop3.POP3SSLStore;
import com.sun.mail.util.BASE64DecoderStream;
import java.io.*;
import java.util.*;
import javax.mail.*;
import javax.mail.internet.*;
// 메일헤더에 포함되는 [FROM, TO, 첨부파일명]은 RFC2047으로 인코딩되어
// 전달되므로 이들 항목이 한글일 경우에는 다시 디코딩하는 것에 주의하세요.
// 예) MimeUtility.decodeText(encodeText)
public class GMailUtilities {
private Session session = null;
private Store store = null;
private String username, password;
private Folder folder;
public GMailUtilities() { }
public void setUserPass(String username, String password) {
this.username = username;
this.password = password;
}
public void connect() throws Exception {
String SSL_FACTORY = "javax.net.ssl.SSLSocketFactory";
Properties pop3Props = new Properties();
pop3Props.setProperty("mail.pop3.socketFactory.class", SSL_FACTORY);
pop3Props.setProperty("mail.pop3.socketFactory.fallback", "false");
pop3Props.setProperty("mail.pop3.port", "995");
pop3Props.setProperty("mail.pop3.socketFactory.port", "995");
URLName url = new URLName("pop3", "pop.gmail.com", 995, "",
username, password);
session = Session.getInstance(pop3Props, null);
store = new POP3SSLStore(session, url);
store.connect();
}
public void openFolder(String folderName) throws Exception {
// Open the Folder
folder = store.getDefaultFolder();
folder = folder.getFolder(folderName); // INBOX
if (folder == null) {
throw new Exception("Invalid folder");
}
// try to open read/write and if that fails try read-only
try {
folder.open(Folder.READ_WRITE);
} catch (MessagingException ex) {
folder.open(Folder.READ_ONLY);
}
}
public void closeFolder() throws Exception {
folder.close(false);
}
public int getMessageCount() throws Exception {
return folder.getMessageCount();
}
public int getNewMessageCount() throws Exception {
return folder.getNewMessageCount();
}
public void disconnect() throws Exception {
store.close();
}
public void printMessage(int messageNo) throws Exception {
System.out.println("Getting message number: " + messageNo);
Message m = null;
try {
m = folder.getMessage(messageNo);
dumpEnvelope(m);
} catch (IndexOutOfBoundsException iex) {
System.out.println("Message number out of range");
}
}
public void printAllMessageEnvelopes() throws Exception {
// Attributes & Flags for all messages ..
Message[] msgs = folder.getMessages();
// Use a suitable FetchProfile
FetchProfile fp = new FetchProfile();
fp.add(FetchProfile.Item.ENVELOPE);
folder.fetch(msgs, fp);
for (int i = 0; i < msgs.length; i++) {
System.out.println("--------------------------");
System.out.println("MESSAGE #" + (i + 1) + ":");
dumpEnvelope(msgs[i]);
}
}
public void printAllMessages() throws Exception {
// Attributes & Flags for all messages ..
Message[] msgs = folder.getMessages();
// Use a suitable FetchProfile
FetchProfile fp = new FetchProfile();
fp.add(FetchProfile.Item.ENVELOPE);
folder.fetch(msgs, fp);
for (int i = 0; i < msgs.length; i++) {
System.out.println("--------------------------");
System.out.println("MESSAGE #" + (i + 1) + ":");
dumpEnvelope(msgs[i]);
}
}
public POP3DTO[] getAllMessages() throws Exception {
POP3DTO[] dtos = null;
// Attributes & Flags for all messages ..
Message[] msgs = folder.getMessages();
dtos = new POP3DTO[msgs.length];
// Use a suitable FetchProfile
FetchProfile fp = new FetchProfile();
fp.add(FetchProfile.Item.ENVELOPE);
folder.fetch(msgs, fp);
for (int i = 0; i < msgs.length; i++) {
dtos[i] = new POP3DTO();
dtos[i].setFrom(MimeUtility.decodeText(msgs[i].getFrom()[0].toString()));
dtos[i].setSubject(msgs[i].getSubject());
dtos[i].setSentDate(msgs[i].getSentDate());
}
return dtos;
}
public static void dumpEnvelope(Message m) throws Exception {
pr(" ");
Address[] a;
// FROM
if ((a = m.getFrom()) != null) {
for (int j = 0; j < a.length; j++)
pr("FROM: " + MimeUtility.decodeText(a[j].toString()));
}
// TO
if ((a = m.getRecipients(Message.RecipientType.TO)) != null) {
for (int j = 0; j < a.length; j++) {
pr("TO: " + MimeUtility.decodeText(a[j].toString()));
}
}
// SUBJECT
pr("SUBJECT: " + m.getSubject());
// DATE
Date d = m.getSentDate();
pr("SendDate: " +
(d != null ? d.toString() : "UNKNOWN"));
// CONTENT TYPE
String contentType = m.getContentType();
pr("CONTENT TYPE: " +contentType);
// CONTENT
// 단순한 구조의 메일은 MimeMessage 안에 바로 텍스트(text/plain, text/html)가 포함되는 구조로 되어있지만,
// 대부분의 메일 내용은 JavaMail 개발자 입장에서 보았을 때 메일본문이 바로 발견되는 것이 아니라
// MimeMessage 내부의 Multipart 안에 몇개의 BodyPart로 구성되어 있고 각 BodyPart 안에는 첨부파일
// 이 있으며, 어떤 BodyPart 안에는 또 다시 Multipart가 포함되고 그 안에 일반텍스(text/plain)나
// HTML(text/html)형식의 메일 본문이 포함되어 있는 경우도 많다.
pr("Message.getContent()-------------------");
pr("CONTENT: "+ m.getContent());
// 첨부파일
if( m.isMimeType("multipart/*") ) {
System.out.println("첨부파일이 포함된 메일");
handleMultipart(m);
//pr(str);
}
}
// 첨부파일을 다루는 메소드, 첨부파일을 로컬시스템에 다운로드한다
// 첨부파일은 Base64 인코딩된 경우와 그렇지 않은 경우로 구분하여 스트림을 사용한다
// MimeMultipart 안에는 다수개의 MimeBodypart(파트)가 포함될 수 있고 각 파트 안에는
// 텍스트(text/plain, text/html), 첨부파일, 또 다른 MimeMultipart 등이 로 존재한다.
// 한개의 파트 안에 또 다른 멀티파트가 포함되어 있을 경우에는 그 멀티파트에 포함되어 있는
// 것은 텍스트나 HTML 이었다.
public static String handleMultipart(Message msg) {
String content = null;
try {
String disposition;
BodyPart part;
Multipart mp = (Multipart) msg.getContent(); // 멀티파트 구함
int mpCount = mp.getCount();
pr("멀티파트 안의 아이템 수:"+mpCount);
for (int m = 0; m < mpCount; m++) {
part = mp.getBodyPart(m); // 멀티파트내의 한개 파트 구함
String partContentType = part.getContentType();
disposition = part.getDisposition(); // 파트의 성격
Object partContent = part.getContent(); // 한개 파트의 내용을 구함
pr(m+1+"번 파트-----disposition:"+disposition);
// 멀티파트 내의 한개 파트가 메일 본문인 경우
if(partContentType.startsWith("text/")&& disposition==null){
String txtContent = part.getContent().toString();
pr("멀티파트 안의 본문:"+txtContent);
} // 멀티파트 내의 한개 파트가 첨부파일이고 파일명이 한글인 경우
else if(partContent instanceof BASE64DecoderStream)
{
BASE64DecoderStream ds = (BASE64DecoderStream)partContent;
String filename = part.getFileName();
// RFC 2047으로 인코딩된 문자열을 다시 디코딩한다.
filename = MimeUtility.decodeText(filename);
pr("한글 파일명:"+filename);
byte[] buf = new byte[ds.available()];
ds.read(buf);
ds.close();
// 첨부파일을 로컬시스템에 다운로드한다
saveLocal(filename, buf);
}
else if (disposition != null &&
(disposition.equals(Part.INLINE)
||disposition.equals(Part.ATTACHMENT)))/*첨부파일명이 영문인 경우*/
{
String filename = part.getFileName();
filename = MimeUtility.decodeText(filename);
pr("영문 파일명:"+filename);
InputStream is = part.getInputStream();
byte[] buf = new byte[is.available()];
is.read(buf);
is.close();
saveLocal(filename, buf);
} else { // 한개의 파트 안에 다시 멀티 파트가 포함된 경우, 대부분 텍스트나 HTML
if(partContent instanceof MimeMultipart){
MimeMultipart multipart = (MimeMultipart)partContent;
int cnt = multipart.getCount();
for(int i=0;i<cnt;i++){
BodyPart bp = multipart.getBodyPart(i);
pr("파트 안의 멀티파트 정보:"+bp.getContentType());
pr("파트 안의 멀티파트 내용:"+bp.getContent());
}
}
}
}// end of for()
} catch (Exception ex) {
ex.printStackTrace();
}
return content;
}
static String indentStr = " ";
static int level = 0;
// Print a, possibly indented, string.
public static void pr(String s) {
System.out.print(indentStr.substring(0, level * 2));
System.out.println(s);
}
private static void saveLocal(String filename, byte[]buf){
FileOutputStream fout = null;
try{
fout = new FileOutputStream("C:/append/"+filename);
fout.write(buf);
fout.close();
}catch(Exception fne){
fne.printStackTrace();
}
}
}