출처:
http://www.berrycracker.net/archives/2133
http://www.mkyong.com/java/how-to-convert-inputstream-to-file-in-java/
JSP 예제에서 Javamail과 gmail 서버를 이용해 메일을 보내는 예제를 공부하고 있었다.
다 쳐서 이제 실행하려는 순간 ClassNotFoundException 예외가 발생하였다.
한참을 찾아본 결과 오타가 원인이었다.
Properties props = new Properties(); /* 중략 */ // props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory()"); props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
저 주석처럼 괄호가 뒤에 붙어서 클래스를 찾을 수 없었다.
그래서 그 괄호를 떼어 다른 결과를 얻을 수 있었다.
보통 이런 뻘짓 하나 찾아내면 다음엔 잘 되었는데 한참을 기다려도 뭐가 되는 게 없었다.
그래서 gmail 포트를 변경하였다.
예제에는 465 포트를 사용하라고 되어 있었지만,
587 포트로 변경하니 일단 접속은 되었다.
그래도 안 되는 거 같아 gmail을 한 번 열어보니 엑세스 문제도 나와 있었다.
일단 본인이기 때문에 보안 수준이 낮은 앱의 엑세스를 허용하였다.
그런데 파일을 보낼려고 하니 파일의 경로를 찾을 수 없다고 나왔다.
찾아보니 html의 form 태그 인코딩을 multipart/form-data로 바꾸라고 해서 바꿨더니,
이제는 input으로 넘겨준 값들이 안 넘어 가버리는 상황까지 이르렀다.
혹시나 해서 request.getParameter()로 확인해보니 애초에 넘어오는 것이 없었다.
여기서 또 다시 한참을 찾아보니 서블릿 3.0부터 지원하기 시작한 multipart/form-data에서
request.getParameter()는 NULL을 리턴해버린다고 한다.
그래서 내가 넘겨준 form 태그 안의 name들을 받기 위해서는 다음과 같은 코드를 사용해야 했다.
List<fileitem> items; InputStream fileContent = null; try { items = new ServletFileUpload(new DiskFileItemFactory()).parseRequest(request); for (FileItem item : items) { if (item.isFormField()) { // input 타입의 값이 file이 아닌 경우 if (item.getFieldName().equals("sender")) sender = item.getString(); if (item.getFieldName().equals("receiver")) receiver = item.getString(); if (item.getFieldName().equals("cc")) cc = item.getString(); if (item.getFieldName().equals("subject")) subject = item.getString(); if (item.getFieldName().equals("contents")) contents = item.getString(); } else { // input 타입의 값이 file인 경우 System.out.println("Filed Name: " + item.getFieldName()); System.out.println("Filed Value (File Name): " + item.getName()); filename = FilenameUtils.getName(item.getName()); fileContent = item.getInputStream(); } } } catch (FileUploadException fe) { // TODO Auto-generated catch block fe.printStackTrace(); } catch (IOException ie) { // TODO: handle exception ie.printStackTrace(); }
저렇게 해서 값을 넘겨주는데는 성공했으나 여전히 파일 위치를 찾을 수 없다고 나왔다.
이에 대해서도 찾아본 결과 아까 위에서 생성하여 파일 내용을 저장한 InputStream을 이용하여
OutputStream으로 이 파일의 사본을 만들어 그걸 보내는 방법이었다.
if (!filename.equals("")) { File file = new File(filename); OutputStream fileWriter = new FileOutputStream(file); int read = 0; byte[] bytes = new byte[1024]; while ((read = fileContent.read(bytes)) != -1) { fileWriter.write(bytes, 0, read); } fileWriter.close(); MimeBodyPart attach = new MimeBodyPart(); // attach.setDataHandler(new DataHandler(file)); // attach the file setting as the file name // attach.setFileName(MimeUtility.encodeWord(file.getName())); attach.attachFile(file); mp.addBodyPart(attach); }
그래서 여기까지 하고 시도해보니 원하는대로 결과가 나왔다.
다음은 해당 내용들을 추가하여 만든 Javamail 예제이다.
1. FormMail.html
보내는 사람과 받는 사람 등의 값을 받는 페이지이다.
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>JSP Form Mail</title> <style type="text/css"> body { font-size: 9pt; } </style> </head> <body> <h2>Java Mail Test</h2> <form name="fmail" method="post" action="SendMail.jsp" enctype="multipart/form-data"> <table border="1" cellpadding="5"> <tr> <td>Sender's E-mail</td> <td><input type="text" name="sender" size="50"></td> </tr> <tr> <td>Receiver's E-mail</td> <td><input type="text" name="receiver" size="50"></td> </tr> <tr> <td>Carbon Copy's E-mail</td> <td><input type="text" name="cc" size="50"></td> </tr> <tr> <td>Mail Title</td> <td><input type="text" name="subject" size="50"></td> </tr> <tr> <td>Attachment File</td> <td><input type="file" name="filename" size="50"></td> </tr> <tr> <td>Mail Context</td> <td> <textarea name="contents" rows="10" cols="40"></textarea> </td> </tr> <tr> <td colspan="2" align="center"> <input type="submit" value="Send Mail"> </td> </tr> </table> </form> </body> </html>
2. SendMail.jsp
FormMail.html을 통해서 받은 내용을 자바빈으로 가져온 Sendmail.java의 메소드에 대입하여
나온 결과에 따른 뒷처리를 하는 페이지이다.
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <jsp:useBean id="smail" scope="page" class="myjsp.mail.Sendmail" /> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>JSP Mail Test</title> </head> <body> <% request.setCharacterEncoding("UTF-8"); %> <% if (smail.mailSender(request)) { out.println("<center><h2>" + "THe mail was sent normally</h2>"); if (!smail.getReceiver().equals("")) out.println("Mail Receiver(To): " + smail.getReceiver() + "<br>"); if (!smail.getCc().equals("")) out.println("Carbon Copy(To): " + smail.getCc()); out.println("<hr><a href='JSPFormMail.jsp'>Write the mail</a></center>"); } else { out.println("<script>alert('Exception occured during sending the mail'); history.go(-1);</script>"); } %> </body> </html>
3. Sendmail.java
실질적으로 메일을 보내기 위한 작업들을 하는 자바빈즈 파일이다.
public class Sendmail { private String sender; private String receiver; private String cc; private String subject; private String contents; private String filename; private String errMsg; public Sendmail() { sender = ""; receiver = ""; cc = ""; subject = ""; contents = ""; filename = ""; errMsg = ""; } public boolean mailSender(HttpServletRequest request) { List<fileitem> items; InputStream fileContent = null; try { items = new ServletFileUpload(new DiskFileItemFactory()).parseRequest(request); for (FileItem item : items) { if (item.isFormField()) { System.out.println("Filed Name: " + item.getFieldName()); System.out.println("Filed Value: " + item.getString()); if (item.getFieldName().equals("sender")) sender = item.getString(); if (item.getFieldName().equals("receiver")) receiver = item.getString(); if (item.getFieldName().equals("cc")) cc = item.getString(); if (item.getFieldName().equals("subject")) subject = item.getString(); if (item.getFieldName().equals("contents")) contents = item.getString(); } else { System.out.println("Filed Name: " + item.getFieldName()); System.out.println("Filed Value (File Name): " + item.getName()); filename = FilenameUtils.getName(item.getName()); fileContent = item.getInputStream(); } } } catch (FileUploadException fe) { fe.printStackTrace(); } catch (IOException ie) { // TODO: handle exception ie.printStackTrace(); } Properties props = new Properties(); props.put("mail.transport.protocol", "smtp"); props.put("mail.smtp.auth", "true"); props.put("mail.smtp.host", "smtp.gmail.com"); props.put("mail.smtp.port", "587"); props.put("mail.smtp.socketFactory.port", "587"); props.put("mail.smtp.starttls.enable", "true"); props.put("mail.smtp.socketFactory.fallback", "false"); props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory"); props.put("mail.smtp.timeout", 5000); java.security.Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider()); try { // 인증된 SMTP 사용자 정보를 가진 Authenticator 객체를 만든다. Authenticator auth = new SMTPAuthenticator(); // props와 auth를 이용해 Session 인스턴스를 만든다. Session session = Session.getDefaultInstance(props, auth); // session을 이용해 MimeMessage 객체를 만든다. MimeMessage msg = new MimeMessage(session); // 헤더를 설정한다. msg.addHeader("Content-Transfer-Encoding", "base64"); msg.addHeader("Reply-To", sender); System.out.println("Sender: " + sender); // 보내는 날짜를 현재 날짜로 설정한다. msg.setSentDate(new Date()); // 보내는 사람의 InternetAddress 객체를 준비한다. InternetAddress from = new InternetAddress(sender); msg.setFrom(from); // 받는 사람이 여러명일 경우 쉼표(,)로 구분한다. if (!receiver.equals("")) { StringTokenizer st = new StringTokenizer(receiver, ","); while (st.hasMoreTokens()) { InternetAddress to = new InternetAddress(st.nextToken()); msg.addRecipient(Message.RecipientType.TO, to); } } // 참조자가 여러명일 경우 쉼표(,)로 구분한다. if (!cc.equals("")) { StringTokenizer stcc = new StringTokenizer(cc, ","); while (stcc.hasMoreTokens()) { InternetAddress tocc = new InternetAddress(stcc.nextToken()); msg.addRecipient(Message.RecipientType.CC, tocc); } } // UTF-8 캐릭터 셋을 사용하여 메일 제목을 작성한다. msg.setSubject(subject, "utf-8"); // MimeMultipart 객체와 MimeBodypart 객체를 사용하여 메일 내용을 작성한다. MimeMultipart mp = new MimeMultipart(); MimeBodyPart body = new MimeBodyPart(); body.setContent(contents, "text/html; charset=utf-8"); mp.addBodyPart(body); // 메일에 첨부파일이 있으면 MimeBodypart 객체를 만든다. // 파일과 MimeBodypart 객체를 MimeMultipart 객체에 추가한다. if (!filename.equals("")) { File file = new File(filename); OutputStream fileWriter = new FileOutputStream(file); int read = 0; byte[] bytes = new byte[1024]; while ((read = fileContent.read(bytes)) != -1) { fileWriter.write(bytes, 0, read); } fileWriter.close(); MimeBodyPart attach = new MimeBodyPart(); attach.attachFile(file); mp.addBodyPart(attach); } msg.setContent(mp, "text/html; charset=utf-8"); msg.saveChanges(); Transport tp = session.getTransport("smtp"); tp.connect(); tp.sendMessage(msg, msg.getAllRecipients()); tp.close(); } catch (MessagingException me) { me.getMessage(); me.printStackTrace(); return false; } catch (Exception e) { e.getMessage(); e.printStackTrace(); return false; } return true; } public String getSender() { return sender; } public String getReceiver() { return receiver; } public String getCc() { return cc; } public String getSubject() { return subject; } public String getContents() { return contents; } public String getFilename() { return filename; } public String getErrMsg() { return errMsg; } } // SMTP 서버에 접속하기 위한 사용자의 ID와 비밀번호를 입력한다. class SMTPAuthenticator extends Authenticator { protected PasswordAuthentication getPasswordAuthentication() { String id = "구글 ID"; // @ 이하는 제외한다. String pw = "구글 비밀번호"; return new PasswordAuthentication(id, pw); } }
확실히 최신 정보를 받기 위해서는 구글이 최고라는 사실을 이번에도 또 느낄 수 있었다.