From 0d50365c7b633337585a8724926b8edbb94f174a Mon Sep 17 00:00:00 2001 From: Jung-ChanYoung <38939015+MyaGya@users.noreply.github.com> Date: Mon, 11 Oct 2021 17:44:01 +0900 Subject: [PATCH] =?UTF-8?q?refactor:=20AWS=20BCC=20=EB=A9=94=EC=9D=BC=20?= =?UTF-8?q?=EB=B0=9C=EC=86=A1=20=EA=B8=B0=EB=8A=A5=20=EB=A6=AC=ED=8E=99?= =?UTF-8?q?=ED=84=B0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apply/application/mail/MailService.kt | 5 +- .../kotlin/apply/infra/mail/AwsMailSender.kt | 87 ++------------- .../apply/infra/mail/MultipartMimeMessage.kt | 100 ++++++++++++++++++ 3 files changed, 112 insertions(+), 80 deletions(-) create mode 100644 src/main/kotlin/apply/infra/mail/MultipartMimeMessage.kt diff --git a/src/main/kotlin/apply/application/mail/MailService.kt b/src/main/kotlin/apply/application/mail/MailService.kt index 6f8d5e127..4c0d3d972 100644 --- a/src/main/kotlin/apply/application/mail/MailService.kt +++ b/src/main/kotlin/apply/application/mail/MailService.kt @@ -3,6 +3,7 @@ package apply.application.mail import apply.application.ApplicationProperties import apply.application.ResetPasswordRequest import apply.domain.user.User +import org.springframework.boot.autoconfigure.mail.MailProperties import org.springframework.core.io.ByteArrayResource import org.springframework.scheduling.annotation.Async import org.springframework.stereotype.Service @@ -13,7 +14,8 @@ import org.thymeleaf.spring5.ISpringTemplateEngine class MailService( private val applicationProperties: ApplicationProperties, private val templateEngine: ISpringTemplateEngine, - private val mailSender: MailSender + private val mailSender: MailSender, + private val mailProperties: MailProperties ) { val MAIL_SENDING_UNIT = 50 @@ -70,6 +72,7 @@ class MailService( @Async fun sendMailsByBCC(request: MailData, files: Map) { + request.recipients.plus(mailProperties.username) for (targetMailsPart in request.recipients.chunked(MAIL_SENDING_UNIT)) { mailSender.sendBcc(targetMailsPart.toTypedArray(), request.subject, request.body, files) } diff --git a/src/main/kotlin/apply/infra/mail/AwsMailSender.kt b/src/main/kotlin/apply/infra/mail/AwsMailSender.kt index 3ba0a0d11..f113e8779 100644 --- a/src/main/kotlin/apply/infra/mail/AwsMailSender.kt +++ b/src/main/kotlin/apply/infra/mail/AwsMailSender.kt @@ -10,24 +10,10 @@ import com.amazonaws.services.simpleemail.model.Body import com.amazonaws.services.simpleemail.model.Content import com.amazonaws.services.simpleemail.model.Destination import com.amazonaws.services.simpleemail.model.Message -import com.amazonaws.services.simpleemail.model.RawMessage import com.amazonaws.services.simpleemail.model.SendEmailRequest -import com.amazonaws.services.simpleemail.model.SendRawEmailRequest import org.springframework.boot.autoconfigure.mail.MailProperties import org.springframework.core.io.ByteArrayResource import org.springframework.stereotype.Component -import java.io.ByteArrayOutputStream -import java.nio.ByteBuffer -import java.util.Properties -import javax.activation.DataHandler -import javax.activation.DataSource -import javax.activation.MimetypesFileTypeMap -import javax.mail.Session -import javax.mail.internet.InternetAddress -import javax.mail.internet.MimeBodyPart -import javax.mail.internet.MimeMessage -import javax.mail.internet.MimeMultipart -import javax.mail.util.ByteArrayDataSource import javax.mail.Message as javaxMessage @Component @@ -66,77 +52,20 @@ class AwsMailSender( body: String, files: Map ) { + val multipartMimeMessage = message { + this.subject = subject + this.userName = mailProperties.username + this.recipient = Recipient(javaxMessage.RecipientType.BCC, toAddresses) + this.body = body + this.files = files + }.build() - var message: MimeMessage? = null - val session = Session.getDefaultInstance(Properties()) - // Create a new MimeMessage object. - message = MimeMessage(session) - // Add subject, from and to lines. - message.setSubject(subject, "UTF-8") - message.setFrom(InternetAddress(mailProperties.username)) // SENDER, example@example.com - message.setRecipients(javaxMessage.RecipientType.TO, toAddresses.map { InternetAddress(it) }.toTypedArray()) - // Create a multipart/alternative child container. - val msgBody = MimeMultipart("alternative") - val wrap = MimeBodyPart() - val textPart = MimeBodyPart() - textPart.setContent(body, "text/plain; charset=UTF-8") - - // val htmlPart = MimeBodyPart() - // htmlPart.setContent(bodyHTML, "text/html; charset=UTF-8") // BODY HTML 은 제외, 제외해도 돌아가는가? - // msgBody.addBodyPart(htmlPart) - msgBody.addBodyPart(textPart) - wrap.setContent(msgBody) - // Create a multipart/mixed parent container. - val msg = MimeMultipart("mixed") - // Add the parent container to the message. - message.setContent(msg) - // Add the multipart/alternative part to the message. - msg.addBodyPart(wrap) - // Define the attachment - - // val fds: DataSource = ByteArrayDataSource(attachment, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") <- 레퍼런스 - for ((fileName, fileData) in files) { - val att = MimeBodyPart() - val fds: DataSource = ByteArrayDataSource( - fileData.byteArray, - MimetypesFileTypeMap().getContentType(fileName) - ) - // val fds: DataSource = FileDataSource("C:\\Users\\kirin\\Desktop\\의문점 1.png") - - att.dataHandler = DataHandler(fds) - att.fileName = fileName - // val reportName = "PhotoReport.xls" <- 이 항목이 있는걸로 보아 att.fileName으로 이름을 붙여줄 수 있는듯 - // att.fileName = reportName - msg.addBodyPart(att) - } - - // Add the attachment to the message. - - // Try to send the email. + val rawEmailRequest = multipartMimeMessage.getRawEmailRequest() try { - // println("Attempting to send an email through Amazon SES " + "using the AWS SDK for Java...") - // Print the raw email content on the console - // Print the raw email content on the console - val out = System.out - message.writeTo(out) - // Send the email. - // Send the email. - val outputStream = ByteArrayOutputStream() - message.writeTo(outputStream) - val rawMessage = RawMessage(ByteBuffer.wrap(outputStream.toByteArray())) - - val rawEmailRequest = SendRawEmailRequest(rawMessage) - // .withConfigurationSetName("ConfigSet") // <- 기본값 - client.sendRawEmail(rawEmailRequest) - // println("Email sent!") - // Display an error if something goes wrong. } catch (ex: Exception) { - println("Email Failed") - println("Error message: ${ex.message}}") ex.printStackTrace() } - println("Email sent with attachment") } private fun createContent(data: String): Content { diff --git a/src/main/kotlin/apply/infra/mail/MultipartMimeMessage.kt b/src/main/kotlin/apply/infra/mail/MultipartMimeMessage.kt new file mode 100644 index 000000000..99d73f64f --- /dev/null +++ b/src/main/kotlin/apply/infra/mail/MultipartMimeMessage.kt @@ -0,0 +1,100 @@ +package apply.infra.mail + +import com.amazonaws.services.simpleemail.model.RawMessage +import com.amazonaws.services.simpleemail.model.SendRawEmailRequest +import org.springframework.core.io.ByteArrayResource +import java.io.ByteArrayOutputStream +import java.nio.ByteBuffer +import java.util.Properties +import javax.activation.DataHandler +import javax.activation.DataSource +import javax.activation.MimetypesFileTypeMap +import javax.mail.Message +import javax.mail.Session +import javax.mail.internet.InternetAddress +import javax.mail.internet.MimeBodyPart +import javax.mail.internet.MimeMessage +import javax.mail.internet.MimeMultipart +import javax.mail.util.ByteArrayDataSource + +data class Recipient(val recipientType: Message.RecipientType, val toAddresses: Array) + +class MultipartMimeMessage(session: Session, private val mimeMixedPart: MimeMultipart) { + val message: MimeMessage = MimeMessage(session) + + fun setSubject(subject: String) { + message.setSubject(subject, "UTF-8") + } + + fun setFrom(userName: String) { + message.setFrom(InternetAddress(userName)) + } + + fun setRecipient(recipient: Recipient) { + message.setRecipients(recipient.recipientType, recipient.toAddresses.map { InternetAddress(it) }.toTypedArray()) + } + + fun addBody(body: String) { + val messageBody = MimeMultipart("alternative") + val wrap = MimeBodyPart() + val textPart = MimeBodyPart() + textPart.setContent(body, "text/plain; charset=UTF-8") + messageBody.addBodyPart(textPart) + wrap.setContent(messageBody) + message.setContent(mimeMixedPart) + mimeMixedPart.addBodyPart(wrap) + } + + fun addAttachment(files: Map) { + for ((fileName, fileData) in files) { + val att = MimeBodyPart() + val fds: DataSource = ByteArrayDataSource( + fileData.byteArray, + findMimeContentTypeByFileName(fileName) + ) + att.dataHandler = DataHandler(fds) + att.fileName = fileName + mimeMixedPart.addBodyPart(att) + } + } + + private fun findMimeContentTypeByFileName(fileName: String): String { + return MimetypesFileTypeMap().getContentType(fileName) + ?: throw IllegalArgumentException("잘못된 확장자입니다.") + } + + fun getRawEmailRequest(): SendRawEmailRequest { + val rawMessage = createRawMessage(message) + return SendRawEmailRequest(rawMessage) + } + + private fun createRawMessage(message: MimeMessage): RawMessage { + val outputStream = ByteArrayOutputStream() + message.writeTo(outputStream) + return RawMessage(ByteBuffer.wrap(outputStream.toByteArray())) + } +} + +data class MultipartMimeMessageBuilder( + var session: Session = Session.getDefaultInstance(Properties()), + var mimeMixedPart: MimeMultipart = MimeMultipart("mixed"), + var subject: String = "", + var userName: String = "", + var recipient: Recipient? = null, + var body: String = "", + var files: Map? = null +) { + fun build(): MultipartMimeMessage { + return MultipartMimeMessage(session, mimeMixedPart).apply { + setSubject(subject) + setFrom(userName) + setRecipient(recipient!!) + addBody(body) + addAttachment(files!!) + } + } +} + +fun message(lambda: MultipartMimeMessageBuilder.() -> Unit): MultipartMimeMessageBuilder { + return MultipartMimeMessageBuilder().apply(lambda) +}