From 662df23d4ddb9260670cb30b88c5eaaffa2a9e36 Mon Sep 17 00:00:00 2001 From: 疯狂的狮子Li <15040126243@163.com> Date: 星期日, 12 三月 2023 18:21:30 +0800 Subject: [PATCH] fix 修复 hutool MailUtils 未适配 jakarta 问题 --- ruoyi-common/ruoyi-common-mail/src/main/java/com/ruoyi/common/mail/utils/InternalMailUtil.java | 108 ++++++++++ ruoyi-common/ruoyi-common-mail/src/main/java/com/ruoyi/common/mail/utils/Mail.java | 486 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 594 insertions(+), 0 deletions(-) diff --git a/ruoyi-common/ruoyi-common-mail/src/main/java/com/ruoyi/common/mail/utils/InternalMailUtil.java b/ruoyi-common/ruoyi-common-mail/src/main/java/com/ruoyi/common/mail/utils/InternalMailUtil.java new file mode 100644 index 0000000..28543c0 --- /dev/null +++ b/ruoyi-common/ruoyi-common-mail/src/main/java/com/ruoyi/common/mail/utils/InternalMailUtil.java @@ -0,0 +1,108 @@ +package com.ruoyi.common.mail.utils; + +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.extra.mail.MailException; +import jakarta.mail.internet.AddressException; +import jakarta.mail.internet.InternetAddress; +import jakarta.mail.internet.MimeUtility; + +import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * 閭欢鍐呴儴宸ュ叿绫� + * @author looly + * @since 3.2.3 + */ +public class InternalMailUtil { + + /** + * 灏嗗涓瓧绗︿覆閭欢鍦板潃杞负{@link InternetAddress}鍒楄〃<br> + * 鍗曚釜瀛楃涓插湴鍧�鍙互鏄涓湴鍧�鍚堝苟鐨勫瓧绗︿覆 + * + * @param addrStrs 鍦板潃鏁扮粍 + * @param charset 缂栫爜锛堜富瑕佺敤浜庝腑鏂囩敤鎴峰悕鐨勭紪鐮侊級 + * @return 鍦板潃鏁扮粍 + * @since 4.0.3 + */ + public static InternetAddress[] parseAddressFromStrs(String[] addrStrs, Charset charset) { + final List<InternetAddress> resultList = new ArrayList<>(addrStrs.length); + InternetAddress[] addrs; + for (String addrStr : addrStrs) { + addrs = parseAddress(addrStr, charset); + if (ArrayUtil.isNotEmpty(addrs)) { + Collections.addAll(resultList, addrs); + } + } + return resultList.toArray(new InternetAddress[0]); + } + + /** + * 瑙f瀽绗竴涓湴鍧� + * + * @param address 鍦板潃瀛楃涓� + * @param charset 缂栫爜锛寋@code null}琛ㄧず浣跨敤绯荤粺灞炴�у畾涔夌殑缂栫爜鎴栫郴缁熺紪鐮� + * @return 鍦板潃鍒楄〃 + */ + public static InternetAddress parseFirstAddress(String address, Charset charset) { + final InternetAddress[] internetAddresses = parseAddress(address, charset); + if (ArrayUtil.isEmpty(internetAddresses)) { + try { + return new InternetAddress(address); + } catch (AddressException e) { + throw new MailException(e); + } + } + return internetAddresses[0]; + } + + /** + * 灏嗕竴涓湴鍧�瀛楃涓茶В鏋愪负澶氫釜鍦板潃<br> + * 鍦板潃闂翠娇鐢�" "銆�","銆�";"鍒嗛殧 + * + * @param address 鍦板潃瀛楃涓� + * @param charset 缂栫爜锛寋@code null}琛ㄧず浣跨敤绯荤粺灞炴�у畾涔夌殑缂栫爜鎴栫郴缁熺紪鐮� + * @return 鍦板潃鍒楄〃 + */ + public static InternetAddress[] parseAddress(String address, Charset charset) { + InternetAddress[] addresses; + try { + addresses = InternetAddress.parse(address); + } catch (AddressException e) { + throw new MailException(e); + } + //缂栫爜鐢ㄦ埛鍚� + if (ArrayUtil.isNotEmpty(addresses)) { + final String charsetStr = null == charset ? null : charset.name(); + for (InternetAddress internetAddress : addresses) { + try { + internetAddress.setPersonal(internetAddress.getPersonal(), charsetStr); + } catch (UnsupportedEncodingException e) { + throw new MailException(e); + } + } + } + + return addresses; + } + + /** + * 缂栫爜涓枃瀛楃<br> + * 缂栫爜澶辫触杩斿洖鍘熷瓧绗︿覆 + * + * @param text 琚紪鐮佺殑鏂囨湰 + * @param charset 缂栫爜 + * @return 缂栫爜鍚庣殑缁撴灉 + */ + public static String encodeText(String text, Charset charset) { + try { + return MimeUtility.encodeText(text, charset.name(), null); + } catch (UnsupportedEncodingException e) { + // ignore + } + return text; + } +} diff --git a/ruoyi-common/ruoyi-common-mail/src/main/java/com/ruoyi/common/mail/utils/Mail.java b/ruoyi-common/ruoyi-common-mail/src/main/java/com/ruoyi/common/mail/utils/Mail.java new file mode 100644 index 0000000..5599be3 --- /dev/null +++ b/ruoyi-common/ruoyi-common-mail/src/main/java/com/ruoyi/common/mail/utils/Mail.java @@ -0,0 +1,486 @@ +package com.ruoyi.common.mail.utils; + +import cn.hutool.core.builder.Builder; +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.io.IORuntimeException; +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.mail.*; +import jakarta.activation.DataHandler; +import jakarta.activation.DataSource; +import jakarta.activation.FileDataSource; +import jakarta.activation.FileTypeMap; +import jakarta.mail.*; +import jakarta.mail.internet.MimeBodyPart; +import jakarta.mail.internet.MimeMessage; +import jakarta.mail.internet.MimeMultipart; +import jakarta.mail.internet.MimeUtility; +import jakarta.mail.util.ByteArrayDataSource; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintStream; +import java.nio.charset.Charset; +import java.util.Date; + +/** + * 閭欢鍙戦�佸鎴风 + * + * @author looly + * @since 3.2.0 + */ +public class Mail implements Builder<MimeMessage> { + private static final long serialVersionUID = 1L; + + /** + * 閭甯愭埛淇℃伅浠ュ強涓�浜涘鎴风閰嶇疆淇℃伅 + */ + private final MailAccount mailAccount; + /** + * 鏀朵欢浜哄垪琛� + */ + private String[] tos; + /** + * 鎶勯�佷汉鍒楄〃锛坈arbon copy锛� + */ + private String[] ccs; + /** + * 瀵嗛�佷汉鍒楄〃锛坆lind carbon copy锛� + */ + private String[] bccs; + /** + * 鍥炲鍦板潃(reply-to) + */ + private String[] reply; + /** + * 鏍囬 + */ + private String title; + /** + * 鍐呭 + */ + private String content; + /** + * 鏄惁涓篐TML + */ + private boolean isHtml; + /** + * 姝f枃銆侀檮浠跺拰鍥剧墖鐨勬贩鍚堥儴鍒� + */ + private final Multipart multipart = new MimeMultipart(); + /** + * 鏄惁浣跨敤鍏ㄥ眬浼氳瘽锛岄粯璁や负false + */ + private boolean useGlobalSession = false; + + /** + * debug杈撳嚭浣嶇疆锛屽彲浠ヨ嚜瀹氫箟debug鏃ュ織 + */ + private PrintStream debugOutput; + + /** + * 鍒涘缓閭欢瀹㈡埛绔� + * + * @param mailAccount 閭欢甯愬彿 + * @return Mail + */ + public static Mail create(MailAccount mailAccount) { + return new Mail(mailAccount); + } + + /** + * 鍒涘缓閭欢瀹㈡埛绔紝浣跨敤鍏ㄥ眬閭欢甯愭埛 + * + * @return Mail + */ + public static Mail create() { + return new Mail(); + } + + // --------------------------------------------------------------- Constructor start + + /** + * 鏋勯�狅紝浣跨敤鍏ㄥ眬閭欢甯愭埛 + */ + public Mail() { + this(GlobalMailAccount.INSTANCE.getAccount()); + } + + /** + * 鏋勯�� + * + * @param mailAccount 閭欢甯愭埛锛屽鏋滀负null浣跨敤榛樿閰嶇疆鏂囦欢鐨勫叏灞�閭欢閰嶇疆 + */ + public Mail(MailAccount mailAccount) { + mailAccount = (null != mailAccount) ? mailAccount : GlobalMailAccount.INSTANCE.getAccount(); + this.mailAccount = mailAccount.defaultIfEmpty(); + } + // --------------------------------------------------------------- Constructor end + + // --------------------------------------------------------------- Getters and Setters start + + /** + * 璁剧疆鏀朵欢浜� + * + * @param tos 鏀朵欢浜哄垪琛� + * @return this + * @see #setTos(String...) + */ + public Mail to(String... tos) { + return setTos(tos); + } + + /** + * 璁剧疆澶氫釜鏀朵欢浜� + * + * @param tos 鏀朵欢浜哄垪琛� + * @return this + */ + public Mail setTos(String... tos) { + this.tos = tos; + return this; + } + + /** + * 璁剧疆澶氫釜鎶勯�佷汉锛坈arbon copy锛� + * + * @param ccs 鎶勯�佷汉鍒楄〃 + * @return this + * @since 4.0.3 + */ + public Mail setCcs(String... ccs) { + this.ccs = ccs; + return this; + } + + /** + * 璁剧疆澶氫釜瀵嗛�佷汉锛坆lind carbon copy锛� + * + * @param bccs 瀵嗛�佷汉鍒楄〃 + * @return this + * @since 4.0.3 + */ + public Mail setBccs(String... bccs) { + this.bccs = bccs; + return this; + } + + /** + * 璁剧疆澶氫釜鍥炲鍦板潃(reply-to) + * + * @param reply 鍥炲鍦板潃(reply-to)鍒楄〃 + * @return this + * @since 4.6.0 + */ + public Mail setReply(String... reply) { + this.reply = reply; + return this; + } + + /** + * 璁剧疆鏍囬 + * + * @param title 鏍囬 + * @return this + */ + public Mail setTitle(String title) { + this.title = title; + return this; + } + + /** + * 璁剧疆姝f枃<br> + * 姝f枃鍙互鏄櫘閫氭枃鏈篃鍙互鏄疕TML锛堥粯璁ゆ櫘閫氭枃鏈級锛屽彲浠ラ�氳繃璋冪敤{@link #setHtml(boolean)} 璁剧疆鏄惁涓篐TML + * + * @param content 姝f枃 + * @return this + */ + public Mail setContent(String content) { + this.content = content; + return this; + } + + /** + * 璁剧疆鏄惁鏄疕TML + * + * @param isHtml 鏄惁涓篐TML + * @return this + */ + public Mail setHtml(boolean isHtml) { + this.isHtml = isHtml; + return this; + } + + /** + * 璁剧疆姝f枃 + * + * @param content 姝f枃鍐呭 + * @param isHtml 鏄惁涓篐TML + * @return this + */ + public Mail setContent(String content, boolean isHtml) { + setContent(content); + return setHtml(isHtml); + } + + /** + * 璁剧疆鏂囦欢绫诲瀷闄勪欢锛屾枃浠跺彲浠ユ槸鍥剧墖鏂囦欢锛屾鏃惰嚜鍔ㄨ缃甤id锛堟鏂囦腑寮曠敤鍥剧墖锛夛紝榛樿cid涓烘枃浠跺悕 + * + * @param files 闄勪欢鏂囦欢鍒楄〃 + * @return this + */ + public Mail setFiles(File... files) { + if (ArrayUtil.isEmpty(files)) { + return this; + } + + final DataSource[] attachments = new DataSource[files.length]; + for (int i = 0; i < files.length; i++) { + attachments[i] = new FileDataSource(files[i]); + } + return setAttachments(attachments); + } + + /** + * 澧炲姞闄勪欢鎴栧浘鐗囷紝闄勪欢浣跨敤{@link DataSource} 褰㈠紡琛ㄧず锛屽彲浠ヤ娇鐢▄@link FileDataSource}鍖呰鏂囦欢琛ㄧず鏂囦欢闄勪欢 + * + * @param attachments 闄勪欢鍒楄〃 + * @return this + * @since 4.0.9 + */ + public Mail setAttachments(DataSource... attachments) { + if (ArrayUtil.isNotEmpty(attachments)) { + final Charset charset = this.mailAccount.getCharset(); + MimeBodyPart bodyPart; + String nameEncoded; + try { + for (DataSource attachment : attachments) { + bodyPart = new MimeBodyPart(); + bodyPart.setDataHandler(new DataHandler(attachment)); + nameEncoded = attachment.getName(); + if (this.mailAccount.isEncodefilename()) { + nameEncoded = InternalMailUtil.encodeText(nameEncoded, charset); + } + // 鏅�氶檮浠舵枃浠跺悕 + bodyPart.setFileName(nameEncoded); + if (StrUtil.startWith(attachment.getContentType(), "image/")) { + // 鍥剧墖闄勪欢锛岀敤浜庢鏂囦腑寮曠敤鍥剧墖 + bodyPart.setContentID(nameEncoded); + } + this.multipart.addBodyPart(bodyPart); + } + } catch (MessagingException e) { + throw new MailException(e); + } + } + return this; + } + + /** + * 澧炲姞鍥剧墖锛屽浘鐗囩殑閿搴斿埌閭欢妯℃澘涓殑鍗犱綅瀛楃涓诧紝鍥剧墖绫诲瀷榛樿涓�"image/jpeg" + * + * @param cid 鍥剧墖涓庡崰浣嶇锛屽崰浣嶇鏍煎紡涓篶id:${cid} + * @param imageStream 鍥剧墖鏂囦欢 + * @return this + * @since 4.6.3 + */ + public Mail addImage(String cid, InputStream imageStream) { + return addImage(cid, imageStream, null); + } + + /** + * 澧炲姞鍥剧墖锛屽浘鐗囩殑閿搴斿埌閭欢妯℃澘涓殑鍗犱綅瀛楃涓� + * + * @param cid 鍥剧墖涓庡崰浣嶇锛屽崰浣嶇鏍煎紡涓篶id:${cid} + * @param imageStream 鍥剧墖娴侊紝涓嶅叧闂� + * @param contentType 鍥剧墖绫诲瀷锛宯ull璧嬪�奸粯璁ょ殑"image/jpeg" + * @return this + * @since 4.6.3 + */ + public Mail addImage(String cid, InputStream imageStream, String contentType) { + ByteArrayDataSource imgSource; + try { + imgSource = new ByteArrayDataSource(imageStream, ObjectUtil.defaultIfNull(contentType, "image/jpeg")); + } catch (IOException e) { + throw new IORuntimeException(e); + } + imgSource.setName(cid); + return setAttachments(imgSource); + } + + /** + * 澧炲姞鍥剧墖锛屽浘鐗囩殑閿搴斿埌閭欢妯℃澘涓殑鍗犱綅瀛楃涓� + * + * @param cid 鍥剧墖涓庡崰浣嶇锛屽崰浣嶇鏍煎紡涓篶id:${cid} + * @param imageFile 鍥剧墖鏂囦欢 + * @return this + * @since 4.6.3 + */ + public Mail addImage(String cid, File imageFile) { + InputStream in = null; + try { + in = FileUtil.getInputStream(imageFile); + return addImage(cid, in, FileTypeMap.getDefaultFileTypeMap().getContentType(imageFile)); + } finally { + IoUtil.close(in); + } + } + + /** + * 璁剧疆瀛楃闆嗙紪鐮� + * + * @param charset 瀛楃闆嗙紪鐮� + * @return this + * @see MailAccount#setCharset(Charset) + */ + public Mail setCharset(Charset charset) { + this.mailAccount.setCharset(charset); + return this; + } + + /** + * 璁剧疆鏄惁浣跨敤鍏ㄥ眬浼氳瘽锛岄粯璁や负true + * + * @param isUseGlobalSession 鏄惁浣跨敤鍏ㄥ眬浼氳瘽锛岄粯璁や负true + * @return this + * @since 4.0.2 + */ + public Mail setUseGlobalSession(boolean isUseGlobalSession) { + this.useGlobalSession = isUseGlobalSession; + return this; + } + + /** + * 璁剧疆debug杈撳嚭浣嶇疆锛屽彲浠ヨ嚜瀹氫箟debug鏃ュ織 + * + * @param debugOutput debug杈撳嚭浣嶇疆 + * @return this + * @since 5.5.6 + */ + public Mail setDebugOutput(PrintStream debugOutput) { + this.debugOutput = debugOutput; + return this; + } + // --------------------------------------------------------------- Getters and Setters end + + @Override + public MimeMessage build() { + try { + return buildMsg(); + } catch (MessagingException e) { + throw new MailException(e); + } + } + + /** + * 鍙戦�� + * + * @return message-id + * @throws MailException 閭欢鍙戦�佸紓甯� + */ + public String send() throws MailException { + try { + return doSend(); + } catch (MessagingException e) { + if (e instanceof SendFailedException) { + // 褰撳湴鍧�鏃犳晥鏃讹紝鏄剧ず鏇村姞璇︾粏鐨勬棤鏁堝湴鍧�淇℃伅 + final Address[] invalidAddresses = ((SendFailedException) e).getInvalidAddresses(); + final String msg = StrUtil.format("Invalid Addresses: {}", ArrayUtil.toString(invalidAddresses)); + throw new MailException(msg, e); + } + throw new MailException(e); + } + } + + // --------------------------------------------------------------- Private method start + + /** + * 鎵ц鍙戦�� + * + * @return message-id + * @throws MessagingException 鍙戦�佸紓甯� + */ + private String doSend() throws MessagingException { + final MimeMessage mimeMessage = buildMsg(); + Transport.send(mimeMessage); + return mimeMessage.getMessageID(); + } + + /** + * 鏋勫缓娑堟伅 + * + * @return {@link MimeMessage}娑堟伅 + * @throws MessagingException 娑堟伅寮傚父 + */ + private MimeMessage buildMsg() throws MessagingException { + final Charset charset = this.mailAccount.getCharset(); + final MimeMessage msg = new MimeMessage(getSession()); + // 鍙戜欢浜� + final String from = this.mailAccount.getFrom(); + if (StrUtil.isEmpty(from)) { + // 鐢ㄦ埛鏈彁渚涘彂閫佹柟锛屽垯浠嶴ession涓嚜鍔ㄨ幏鍙� + msg.setFrom(); + } else { + msg.setFrom(InternalMailUtil.parseFirstAddress(from, charset)); + } + // 鏍囬 + msg.setSubject(this.title, (null == charset) ? null : charset.name()); + // 鍙戦�佹椂闂� + msg.setSentDate(new Date()); + // 鍐呭鍜岄檮浠� + msg.setContent(buildContent(charset)); + // 鏀朵欢浜� + msg.setRecipients(MimeMessage.RecipientType.TO, InternalMailUtil.parseAddressFromStrs(this.tos, charset)); + // 鎶勯�佷汉 + if (ArrayUtil.isNotEmpty(this.ccs)) { + msg.setRecipients(MimeMessage.RecipientType.CC, InternalMailUtil.parseAddressFromStrs(this.ccs, charset)); + } + // 瀵嗛�佷汉 + if (ArrayUtil.isNotEmpty(this.bccs)) { + msg.setRecipients(MimeMessage.RecipientType.BCC, InternalMailUtil.parseAddressFromStrs(this.bccs, charset)); + } + // 鍥炲鍦板潃(reply-to) + if (ArrayUtil.isNotEmpty(this.reply)) { + msg.setReplyTo(InternalMailUtil.parseAddressFromStrs(this.reply, charset)); + } + + return msg; + } + + /** + * 鏋勫缓閭欢淇℃伅涓讳綋 + * + * @param charset 缂栫爜锛寋@code null}鍒欎娇鐢▄@link MimeUtility#getDefaultJavaCharset()} + * @return 閭欢淇℃伅涓讳綋 + * @throws MessagingException 娑堟伅寮傚父 + */ + private Multipart buildContent(Charset charset) throws MessagingException { + final String charsetStr = null != charset ? charset.name() : MimeUtility.getDefaultJavaCharset(); + // 姝f枃 + final MimeBodyPart body = new MimeBodyPart(); + body.setContent(content, StrUtil.format("text/{}; charset={}", isHtml ? "html" : "plain", charsetStr)); + this.multipart.addBodyPart(body); + + return this.multipart; + } + + /** + * 鑾峰彇榛樿閭欢浼氳瘽<br> + * 濡傛灉涓哄叏灞�鍗曚緥鐨勪細璇濓紝鍒欏叏灞�鍙厑璁镐竴涓偖浠跺笎鍙凤紝鍚﹀垯姣忔鍙戦�侀偖浠朵細鏂板缓涓�涓柊鐨勪細璇� + * + * @return 閭欢浼氳瘽 {@link Session} + */ + private Session getSession() { + final Session session = MailUtils.getSession(this.mailAccount, this.useGlobalSession); + + if (null != this.debugOutput) { + session.setDebugOut(debugOutput); + } + + return session; + } + // --------------------------------------------------------------- Private method end +} -- Gitblit v1.9.3