[Java] 메일에 엑셀파일 담아서 발송하는 방법[메일 첨부 파일/gmail]

2022-09-02


Photo by Camilo Contreras on Unsplash

단순 메일 발송이 아닌 첨부파일 그중에서 엑셀 파일과 함께 발송하는 방법을 알아보자. 우선적으로 구현 class는 총 3개이며, 메일 계정 인증 유틸 / 메일 발송 유틸 / 실제 메일을 발송하는 컨트롤러 이렇게 구성했다.


0. 라이브러리

 

자신의 build.gradle 아래 2가지의 라이브러리를 추가해야 한다.

 

 

	// 엑셀관련 workbook 라이브러리
	// https://mvnrepository.com/artifact/org.apache.poi/poi
	implementation group: 'org.apache.poi', name: 'poi', version: '5.2.2'
	// https://mvnrepository.com/artifact/org.apache.poi/poi-ooxml
	implementation group: 'org.apache.poi', name: 'poi-ooxml', version: '5.2.2'

1. 메일 계정 인증

 

계정 인증 단에 클래스이다. 사용자 아이디와 비밀번호를 파라미터로 받아 인증정보를 생성한다.

 

import javax.mail.Authenticator;
import javax.mail.PasswordAuthentication;

public class MailAuthUtil extends Authenticator{

	//비밀번호 인증
	PasswordAuthentication pa;
	
    //파라미터 있는 경우 생성자
    public MailAuthUtil(String id, String pw) {  	
    	String sendId =id ; 
    	String sendPw =pw;
        System.out.println("sendId -> " + sendId);
        System.out.println("sendPw -> " + sendPw);
        pa = new PasswordAuthentication(sendId, sendPw);
    }
    
    //발송단계에서 인증 정보를 확인할 수 있음
    public PasswordAuthentication getPasswordAuthentication() {
        return pa;
    }
}

2. 메일발송/엑셀파일 생성 유틸

 

메일 발송과 발송할 엑셀파일을 만드는 메서드가 포함되어 있는 유틸 클래스이다. 관련 내용은 주석으로 적어두었다.

 

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.mail.Authenticator;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.internet.MimeUtility;
import javax.mail.util.ByteArrayDataSource;

import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

public class MailSendUtil {
	
	//메일을 보내는 메소드(메일 계정 순서, 수신자)
	public String mailSend(
			MailAuthUtil MailAuthUtil
			, String recevier
			, String content
			, String title
			, Workbook wb) {

		Properties prop = System.getProperties();
		
        
		prop.put("mail.smtp.host", "smtp.gmail.com"); // 이메일 발송을 처리해줄 SMTP서버
		prop.put("mail.smtp.starttls.enable", "true"); // TLS 통신여부 확인
		prop.put("mail.smtp.auth", "true"); // SMTP 인증 여부
		prop.put("mail.smtp.port", "587"); // TLS port 정보 입력 : 587
		
		System.out.println("username info -> " + MailAuthUtil.getPasswordAuthentication().getUserName());
		System.out.println("userPw info -> " + MailAuthUtil.getPasswordAuthentication().getPassword());
		Authenticator auth = MailAuthUtil;
        Session session = Session.getDefaultInstance(prop, auth); // 디폴트 세션을 계속 사용하도록 설정
//		Session session = Session.getInstance(prop, auth); //항상 새로운 메일 객체 생성하도록 설정

		MimeMessage mimeMessage = new MimeMessage(session);

		try {
			
			mimeMessage.setSentDate(new Date());// 보내는 날짜 지정

			
			System.out.println("sender address -> " +MailAuthUtil.getPasswordAuthentication().getUserName());
			mimeMessage.setFrom(new InternetAddress(MailAuthUtil.getPasswordAuthentication().getUserName(), "테스트계정", "UTF-8")); // 발송자를 지정한다. 발송자의 메일, 발송자명
			
            // 수신자의 메일을 생성한다.
			InternetAddress receiverAddress = new InternetAddress(recevier);
			
			// Message.RecipientType.TO : 받는 수신자
			// Message.RecipientType.CC : 참조 수신자
			mimeMessage.setRecipient(Message.RecipientType.TO, receiverAddress); //수신정보 설정

			mimeMessage.setSubject(title, "UTF-8");             // 메일의 제목 지정		
			Multipart mp = new MimeMultipart(); //멀티파트 객체 생성
			
			System.out.println(" ===== file info body info setting . . .  ===== ");
			MimeBodyPart mbp1 = new MimeBodyPart();
			mbp1.setContent(content, "text/html; charset=utf-8"); //메일 내용 셋팅
			mp.addBodyPart(mbp1); // 파일송신시 보이는 텍스트 파일 멀티파트 담는다.
			
			//만들어진 엑셀파일을 읽어들인다.
			ByteArrayOutputStream bos = new ByteArrayOutputStream();
			try {
				wb.write(bos);
			} catch (IOException e) {
				e.printStackTrace();
			}

			//파일명 + 날짜
			SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
			Date now = new Date();
			String nowStr = dateFormat.format(now);
			
			//excel 형식의 파일전달
			DataSource fds = new ByteArrayDataSource(bos.toByteArray(), "application/vnd.ms-excel");
			MimeBodyPart attachment = new MimeBodyPart();
			attachment.setFileName(MimeUtility.encodeText("테스트파일_" + nowStr, "UTF-8","B"));  // 파일이름설정
			attachment.setDataHandler(new DataHandler(fds)); // 전달파일 핸들러			
			
			//멀티파트 형식으로 파일정보 추가
			mp.addBodyPart(attachment);
			//메일 전달내용에 해당 멀티파트(엑셀파일) 정보 추가
			mimeMessage.setContent(mp);
			System.out.println(" ===== file info setting end  ===== ");
			//메일 발송
			Transport.send(mimeMessage);
		} catch (AddressException ae) {
			System.out.println("AddressException : " + ae);
			return "fail";
		} catch (MessagingException me) {
			System.out.println("MessagingException : " + me);
			return "fail";
		} catch (UnsupportedEncodingException e) {
			System.out.println("UnsupportedEncodingException : " + e);
			return "fail";
		}
		return "success";
	}
	
	
	public Workbook makeEmailFormatListExcel(List<Map<String, String>> emailFormatList) {
		Workbook wb = new XSSFWorkbook();
		System.out.println("emailFormatList size -> "+  emailFormatList.size());
		Sheet sheet = wb.createSheet("info");

		int rownum = 0;
		int cellnum = 0;

		Row row = sheet.createRow(rownum++);
		row.createCell(cellnum++).setCellValue("테스트이름");
		row.createCell(cellnum++).setCellValue("테스트나이");

		//데이터 루프 문 시작 가져온 데이터 만큼 반복
		for(Map<String, String> emailFormatInfo: emailFormatList) {
			System.out.println("emailFormatInfo -> " + emailFormatInfo.toString());
			row = sheet.createRow(rownum++);

			cellnum = 0;

			row.createCell(cellnum++).setCellValue(emailFormatInfo.get("testName"));
			row.createCell(cellnum++).setCellValue(emailFormatInfo.get("testAge"));
		}
		System.out.println("email info file make end");
		return wb;
	}
}

2-2. 2개 이상의 메일 계정 사용시

 

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.mail.Authenticator;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.internet.MimeUtility;
import javax.mail.util.ByteArrayDataSource;

import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

public class MailSendUtil {
	
	//메일을 보내는 메소드(메일 계정 순서, 수신자)
	public String mailSend(
			MailAuthUtil MailAuthUtil
			, String recevier
			, String content
			, String title
			, Workbook wb) {

		Properties prop = System.getProperties();
		
        
//		prop.put("mail.smtp.host", "smtp.gmail.com"); // 이메일 발송을 처리해줄 SMTP서버
		prop.put("mail.smtp.starttls.enable", "true"); // TLS 통신여부 확인
		prop.put("mail.transport.protocol", "smtp");
		prop.put("mail.smtp.auth", "true"); // SMTP 인증 여부
		prop.put("mail.smtp.port", "587"); // TLS port 정보 입력 : 587
		
		System.out.println("username info -> " + MailAuthUtil.getPasswordAuthentication().getUserName());
		System.out.println("userPw info -> " + MailAuthUtil.getPasswordAuthentication().getPassword());
//		Authenticator auth = MailAuthUtil;

		String SendId = newMerchantSettlementEmailSendUtils.getPasswordAuthentication().getUserName();
		String SendPw = newMerchantSettlementEmailSendUtils.getPasswordAuthentication().getPassword();

		Session session = Session.getDefaultInstance(prop); 
		MimeMessage mimeMessage = new MimeMessage(session);

		//메일 발송	객체 생성
		Transport transport = null;	
		try {
			
			mimeMessage.setSentDate(new Date());// 보내는 날짜 지정

			
			System.out.println("sender address -> " +MailAuthUtil.getPasswordAuthentication().getUserName());
			mimeMessage.setFrom(new InternetAddress(MailAuthUtil.getPasswordAuthentication().getUserName(), "테스트계정", "UTF-8")); // 발송자를 지정한다. 발송자의 메일, 발송자명
			
            // 수신자의 메일을 생성한다.
			InternetAddress receiverAddress = new InternetAddress(recevier);
			
			// Message.RecipientType.TO : 받는 수신자
			// Message.RecipientType.CC : 참조 수신자
			mimeMessage.setRecipient(Message.RecipientType.TO, receiverAddress); //수신정보 설정

			mimeMessage.setSubject(title, "UTF-8");             // 메일의 제목 지정
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
			try {
				wb.write(baos); //엑셀파일 바이트 스트립에 기록
			} catch (IOException e) {
				e.printStackTrace();
			}
			
			Multipart mp = new MimeMultipart(); //멀티파트 객체 생성
			
			System.out.println(" ===== file info body info setting . . .  ===== ");
			MimeBodyPart mbp1 = new MimeBodyPart();
			mbp1.setContent(content, "text/html; charset=utf-8"); //메일 내용 셋팅
			mp.addBodyPart(mbp1); // 파일송신시 보이는 텍스트 파일 멀티파트 담는다.
			
			//만들어진 엑셀파일을 읽어들인다.
			ByteArrayOutputStream bos = new ByteArrayOutputStream();
			try {
				wb.write(bos);
			} catch (IOException e) {
				e.printStackTrace();
			}

			//파일명 + 날짜
			SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
			Date now = new Date();
			String nowStr = dateFormat.format(now);
			
			//excel 형식의 파일전달
			DataSource fds = new ByteArrayDataSource(bos.toByteArray(), "application/vnd.ms-excel");
			MimeBodyPart attachment = new MimeBodyPart();
			attachment.setFileName(MimeUtility.encodeText("테스트파일_" + nowStr, "UTF-8","B"));  // 파일이름설정
			attachment.setDataHandler(new DataHandler(fds)); // 전달파일 핸들러			
			
			//멀티파트 형식으로 파일정보 추가
			mp.addBodyPart(attachment);
			//메일 전달내용에 해당 멀티파트(엑셀파일) 정보 추가
			mimeMessage.setContent(mp);
			System.out.println(" ===== file info setting end  ===== ");
			//메일 발송
			System.out.println(" ===== file info setting end  ===== ");
			transport.connect("smtp.gmail.com", SendId, SendPw);;
			transport.sendMessage(mimeMessage, mimeMessage.getAllRecipients());	
		} catch (AddressException ae) {
			System.out.println("AddressException : " + ae);
			return "fail";
		} catch (MessagingException me) {
			System.out.println("MessagingException : " + me);
			return "fail";
		} catch (UnsupportedEncodingException e) {
			System.out.println("UnsupportedEncodingException : " + e);
			return "fail";
		}finally {
			try {
				transport.close();
				System.out.println("After Mail Send [transport Close Success]");
			} catch (MessagingException e) {
				System.out.println("메일 세션 종료 오류 발생 " + e);
			}
		}
		return "success";
	}
	
	
	public Workbook makeEmailFormatListExcel(List<Map<String, String>> emailFormatList) {
		Workbook wb = new XSSFWorkbook();
		System.out.println("emailFormatList size -> "+  emailFormatList.size());
		Sheet sheet = wb.createSheet("info");

		int rownum = 0;
		int cellnum = 0;

		Row row = sheet.createRow(rownum++);
		row.createCell(cellnum++).setCellValue("테스트이름");
		row.createCell(cellnum++).setCellValue("테스트나이");

		//데이터 루프 문 시작 가져온 데이터 만큼 반복
		for(Map<String, String> emailFormatInfo: emailFormatList) {
			System.out.println("emailFormatInfo -> " + emailFormatInfo.toString());
			row = sheet.createRow(rownum++);

			cellnum = 0;

			row.createCell(cellnum++).setCellValue(emailFormatInfo.get("testName"));
			row.createCell(cellnum++).setCellValue(emailFormatInfo.get("testAge"));
		}
		System.out.println("email info file make end");
		return wb;
	}
}

3. 컨트롤러

 

컨트롤러에서 실제 테스트를 돌려 볼 수 있게끔 구성하였다.

 

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class TestController {

	@GetMapping("/test/mail/send")
	public void testMail() {
		String id = "보내는사람아이디"; //ex) test@test.com
		String pw = "보내는사람비밀번호";

		//https://myaccount.google.com/lesssecureapps
		//해당 링크에서 보안 수준이 낮은 앱 엑세스 허용
		
		//발송자 인증객체 생성
		MailAuthUtil mailAuthUtil = new MailAuthUtil(id, pw);

		//메일 발송을 위한 객체 생성
		MailSendUtil MailSendUtil = new MailSendUtil();

		//엑셀파일 생성을 위한 리스트 해쉬맵 객체 생성
		Map<String, String> map1 = new HashMap<String, String>();
		map1.put("testName", "test1");
		map1.put("testAge", "99");
		Map<String, String> map2 = new HashMap<String, String>();
		map2.put("testName", "test2");
		map2.put("testAge", "100");;
		List<Map<String, String>> list = new ArrayList<Map<String,String>>();
		list.add(map1);
		list.add(map2);

		//엑셀파일 생성
		Workbook wb = MailSendUtil.makeEmailFormatListExcel(list);

		//수신자 정보 설정
		String title = "테스트 제목 메일입니다.";
		String content = "테스트 내용 메일입니다.";
		String recevier ="받는사람이메일주소"; //ex) test@test.com

		String result = "";

		//메일발송
		result = MailSendUtil.mailSend(mailAuthUtil, recevier, content, title, wb);
		System.out.println("mail send -> " + result);


	}
}

//https://myaccount.google.com/lesssecureapps
//해당 링크에서 보안 수준이 낮은 앱 엑세스 허용

 

위의 주석을 참고해서 아래와 같이 보안 수준이 낮은 앱의 액세스를 허용해주어야 한다. 해당 액세스는 발송자 이메일 계정에서 설정해야 한다.(설정 안하면 메일 발송 안됨 -> 인증 과정에 막히게 된다.)

 

 

 


메인 이미지 출처 : Photo by Camilo Contreras on Unsplash