이전에 JavaMail을 이용해서 간단하게 메일을 보내는 방법을 살펴 보았습니다. 이번에는 메일에 파일을 첨부하여 전송하는 방법에 대해서 알아 보겠습니다.
우선 메일에 파일을 첨부하는 것은 웹서버가 존재하는 로컬 파일시스템에 파일을 전송한 다음 전송된 파일을 메일에 첨부해서 MTA(메일서버)에 보내는 방식을 취합니다. 단순히 로컬에서 파일을 보낸다면 그냥 첨부를 시키면 되겠지만, 웹서버가 MUA(메일 클라이언트)의 역할을 해야하므로 이러한 방식을 취한다고 생각하시면 됩니다.
파일 업로드를 어떻게 할 것인가를 결정해야 하는데, 몇몇 파일 업로드 컴포넌트들이 배포되고 있으며 이미 상당기간 동안 검증을 거쳤으므로 직접 제작하는 것보다 이들을 이용하면 좋을 것입니다. 개인적으로는 com.oreilly.servlet 패키지를 사용하는 것을 좋아하며 추천합니다. SmartUpload와 같은 컴포넌트를 사용하셔도 되고, 직접 제작해서 사용하셔도 될 것입니다.
우리는 com.oreilly.servlet 패키지를 이용하도록 합시다. 이 패키지는 http://www.servlets.com 에서 다운로드 하시기 바랍니다. zip파일을 풀면 아래와 같이 폴더리스트가 구성됩니다.

위 화면에서 보시는 바와 같이 cos-19Jun2001/lib폴더에 있는 cos.jar 파일을 역시 클래스 패스가 가능하도록 설정하시면 됩니다.
이제 우리가 JavaMail(1)에서 사용했던 jsp 페이지와 서블릿 클래스를 약간 수정해야 합니다. 먼저 jsp 페이지에서 수정해야 할 부분입니다.
<form method="post" action="/servlet/MailServlet" enctype="multipart/form-data">
form 태그 내부에서 enctype을 multipart/form-data 로 설정하시면 됩니다. 그 다음으로<input type="file" name="file01">을 추가하도록 하시지요.
전체적으로 수정된 JSP 페이지는 아래와 같습니다.
<%@ page contentType="text/html;charset=EUC-KR" %> <% Boolean success = (Boolean)request.getAttribute("success"); %> <HTML> <HEAD> <TITLE> Mailer </TITLE> </HEAD> <BODY> <center> <h1> 간단한 메일 전송 </h1> <form method="post" action="/servlet/MailServlet" enctype="multipart/form-data"> <table> <tr> <td>from </td> <td><input type="text" name="from" value="bnote@naver.com"></td> </tr> <tr> <td>to </td> <td><input type="text" name="to" value="bluenote@uniboss.com"></td> </tr> <tr> <td>subject </td> <td><input type="text" name="subject" value="테스트 메일"></td> </tr> <tr> <td> content </td> <td><textarea name="content" rows=10 cols=30>허떱한 메일 보내기</textarea> </td> </tr> <tr> <td> file </td> <td><input type="file" name="file01"></td> </tr> <tr><td colspan=2 align=center><input type="submit" value="Transport"> </td> </tr> </table> </form> <% if(success != null && success.booleanValue() == false){ %> 메일 전송 실패하였습니다. <% }else if(success != null && success.booleanValue() == true){ %> 정상적으로 전송되었습니다.<%}%> </BODY> </HTML> |
수정한 화면은 아래와 같지만 여전히 허덥해 보입니다.

이제 servlet 클래스만 수정하시면 됩니다. 앞서 말씀드렸듯이 파일 업로드 후 첨부이므로 업로드를 위한 클래스를 사용해야 합니다. 그리고 첨부에 관련된 클래스는 JavaBeans Activation Framework API에 있습니다. 따라서 두가지 패키지를 더 import 해야 합니다.
import javax.activation.*;
import com.oreilly.servlet.*;
import com.oreilly.servlet.*;
파일 업로드를 위해서, 그리고 multipart/form-data로 넘어오는 파라미터들을 파싱하기 위해서 com.oreilly.servlet.MultipartRequest 클래스를 사용하도록 하겠습니다.
private static final String FILE_PATH = "c:/temp/";
private static final int MAX_SIZE = 5*1024*1024; //5MByte
private static final int MAX_SIZE = 5*1024*1024; //5MByte
MultipartRequest request = new MultipartRequest(req, FILE_PATH, MAX_SIZE);
아규먼트로 세 가지(HttpServletRequest, 업로드할 파일의 경로, 업로드할 파일의 최대 크기)를 지정하셔야 합니다. 그래서 두 가지 변수를 더 설정하고 MultipartRequest 객체를 생성하였습니다. 여기서 req는 HttpServletRequest 입니다.
이제 파라미터들은 request에서 얻을 수 있습니다.
String to = request.getParameter("to");
String from = request.getParameter("from");
String subject = request.getParameter("subject");
String content = request.getParameter("content");
String from = request.getParameter("from");
String subject = request.getParameter("subject");
String content = request.getParameter("content");
"파일 첨부를 하면서 우리의 메시지는 이제 Multipart 가 되었습니다."라고 생각해 주세요. 다중 부분이 생긴 메시지라는 의미로 생각하시면 될 듯 합니다. 이 Multipart의 subClass인 MimeMultipart는 MimeBodyPart를 자식으로 가집니다. 전달하려는 단순메시지 혹은 HTML이 하나의 BodyPart이고, 파일 하나가 또 하나의 BodyPart입니다. 따라서 우리는 두개의 BodyPart를 MultiPart에 넣고 이 MultiPart를 Message의 Content에 담아서 보내면 되는 것입니다.
조금 생소하게 들릴지 모르지만 내용을 음미하면서 다시 한번 읽어보시고 소스 코드를 보시길 권장합니다.
MimeBodyPart mbp01 = new MimeBodyPart(); mbp01.setContent(content, "text/html;charset=EUC-KR"); Multipart mp = new MimeMultipart(); mp.addBodyPart(mbp01); if(request.getFile("file01") !=null){ MimeBodyPart mbp02 = new MimeBodyPart(); FileDataSource fds = new FileDataSource(request.getFile("file01")); mbp02.setDataHandler(new DataHandler(fds)); mbp02.setFileName(fds.getName()); fileName = fds.getName(); //임시파일 삭제를 위해 파일이름을 설정 mp.addBodyPart(mbp02); } ms.setContent(mp); |
위 코드에서 BodyPart의 content를 지정하는 것도 setContent() 메쏘드입니다. 그리고 MultipartRequest의 getFile() 메쏘드가 업로드된 파일을 가져오는 메쏘드입니다. 반환형이 java.io.File이거덩요. 다른 메쏘드에 대해서는 API 문서를 참조하시구요. 이 파일을 javax.activation.FileDataSource의 생성자의 아규먼트로 사용합니다. 아래 부분 처럼 말이죠. file01은 jsp 페이지에서 <file />에서 name 속성에 지정한 것입니다.
FileDataSource fds = new FileDataSource(request.getFile("file01"));
파일 이름은 FileDataSource에서 얻고 BodyPart의 setFileName()으로 파일명을 지정합니다. Multipart에 BodyPart를 추가하고 Message에 Multipart를 추가하면 상황 종료입니다.
Transport.send(ms);
위와 같이 전송하는 부분은 동일합니다. 마지막으로 업로드된 파일을 지워버려야 하므로 메쏘드를 하나 만들어야 할것입니다. 저는 아래와 같이 만들었습니다.
private synchronized void deleteFile(String fileName){
File f = new File(FILE_PATH+fileName);
if(fileName !=null && f.exists())
f.delete();
}
File f = new File(FILE_PATH+fileName);
if(fileName !=null && f.exists())
f.delete();
}
그리고 Transport.send(ms)를 호출한 다음 deleteFile(fileName);을 호출합니다. 전체적인 소스는 아래와 같습니다. 이전에 JavaMail(1)에서 사용한 소스 코드 중 필요없는 부분은 주석으로 처리했습니다.
import javax.servlet.*; import javax.servlet.http.*; import javax.mail.*; import javax.mail.internet.*; import java.io.*; import java.util.*; import javax.activation.*; import com.oreilly.servlet.*; public class MailServlet extends HttpServlet{ private static final String SMTP_HOST = "ns.uniboss.com"; private static final String FILE_PATH = "c:/temp/"; private static final int MAX_SIZE = 5*1024*1024; //5MByte public void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException{ MultipartRequest request = new MultipartRequest(req, FILE_PATH, MAX_SIZE); String to = request.getParameter("to"); String from = request.getParameter("from"); String subject = request.getParameter("subject"); String content = request.getParameter("content"); /* String to = req.getParameter("to"); String from = req.getParameter("from"); String subject = req.getParameter("subject"); String content = req.getParameter("content"); */ Properties props = new Properties(); props.put("mail.host", SMTP_HOST); Boolean success = null; String fileName = null; try{ Session session= Session.getDefaultInstance(props); Message ms = new MimeMessage(session); ms.setFrom(new InternetAddress(from)); ms.setRecipient(Message.RecipientType.TO, new InternetAddress(to)); ms.setSubject(new String(subject.getBytes("8859_1"), "KSC5601")); //ms.setContent(content, "text/plain"); MimeBodyPart mbp01 = new MimeBodyPart(); mbp01.setContent(content, "text/html;charset=EUC-KR"); Multipart mp = new MimeMultipart(); mp.addBodyPart(mbp01); if(request.getFile("file01") !=null){ MimeBodyPart mbp02 = new MimeBodyPart(); FileDataSource fds = new FileDataSource(request.getFile("file01")); mbp02.setDataHandler(new DataHandler(fds)); mbp02.setFileName(fds.getName()); fileName = fds.getName(); //임시파일 삭제를 위해 파일이름을 설정 mp.addBodyPart(mbp02); } ms.setContent(mp); Transport.send(ms); success = new Boolean(true); deleteFile(fileName); }catch(Exception e){ success = new Boolean(false); System.out.println(e); }finally{ req.setAttribute("success", success); gotoPage("/mail.jsp", req, res); } } public void gotoPage(String address, HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException{ RequestDispatcher rd = getServletContext().getRequestDispatcher(address); rd.forward(req, res); } private synchronized void deleteFile(String fileName){ File f = new File(FILE_PATH+fileName); if(fileName !=null && f.exists()) f.delete(); } } |
지금까지는 파일 하나만 전송하는 예인데 다중 파일 첨부도 별로 틀린 것은 없습니다.
MultipartRequest에는 public Enumeration getFileNames() 메쏘드가 있는데, Enumeration을 루프돌면서 BodyPart를 생성하고 첨부하면 됩니다.
역시 파일들을 삭제하려면 배열이나 Vector 등 Collection에 담아 놓고 나중에 루프를 돌면서 지우시면 됩니다.
역시 파일들을 삭제하려면 배열이나 Vector 등 Collection에 담아 놓고 나중에 루프를 돌면서 지우시면 됩니다.
한가지 유의하실 점은 Transport.send()가 호출되기 전에 파일을 지우면 NullPointerException이 발생할 것이며, 전송한 후에는 getFileNames()을 호출하여도 이미 모두 초기화가 되어 버려서 Null을 리턴할 것입니다. 따라서 이 파일목록도 다른 곳에 저장하고 처리하시면 될 듯 합니다.
메일 전송과 관련해서는 JavaMail(1,2)를 통해서 알아 보았습니다. 웹메일 전송에 필요한 큰 줄기를 살펴보았는데 응용해서 여러가지 해보시기 바랍니다.
댓글 없음:
댓글 쓰기