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(); } } }