add 添加邮件附件压缩方法,支持 本地文件/目录+oss网络路径 一键打包

This commit is contained in:
bleachtred 2023-06-13 09:55:18 +08:00
parent 6ffe9473f7
commit e2142aaa23
4 changed files with 215 additions and 2 deletions

View File

@ -37,6 +37,18 @@ public interface MailClient {
*/
void sendEmail(String mailAddress, String title, String body,Map<String,String> files);
/**
* sendEmail
* <p>发送带有附件的文本邮件
* @param mailAddress 收件人地址 多个收件人地址请按英文','字符隔开
* @param title 邮件标题
* @param body 邮件正文
* @param zipName 压缩包名称 比如 附件.zip
* @param files 附件可添加多个
* @author :Wind
*/
void sendEmail(String mailAddress, String title, String body, String zipName, Map<String,String> files);
/**
* sendEmail
* <p>群体发送带有附件的文本邮件
@ -220,6 +232,21 @@ public interface MailClient {
*/
void sendHtml(String mailAddress, String title ,String body, String htmlName, Map<String,String> parameter,Map<String,String> files);
/**
* sendHtml
* <p> 读取模板发送html邮件,并携带正文和附件
* <p> 将默认读取resources/template下的html文件第四个参数为html的名称需携带尾缀
* @param mailAddress 收件人地址 多个收件人地址请按英文','字符隔开
* @param title 邮件标题
* @param body 邮件文本正文 可为空
* @param htmlName 邮件正文
* @param parameter key为模板的变量名称 无需携带大括号 value为模板变量所对应的值
* @param zipName 压缩包名称 比如 附件.zip
* @param files 附件可添加多个 key 为文件名value为文件的路径
* @author :Wind
*/
void sendHtml(String mailAddress, String title, String body, String htmlName, Map<String,String> parameter, String zipName, Map<String,String> files);
/**
* sendHtml
* <p> 读取模板发送html邮件,并携带正文和附件

View File

@ -22,6 +22,11 @@
<groupId>cn.hutool</groupId>
<artifactId>hutool-cron</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-http</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,107 @@
package org.dromara.email.comm.utils;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.ZipUtil;
import cn.hutool.http.HttpUtil;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
/**
* 压缩包处理类
*
* @author Bleachtred
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class ZipUtils extends ZipUtil {
private final static Integer TEMP_SIZE = 2048;
/**
* 压缩方法支持 本地文件/目录 + oss网络路径 混合
* @param files 文件列表
* @author Bleachtred
*/
public static void zipFilePip(Map<String, String> files, OutputStream outputStream) {
try(WritableByteChannel out = Channels.newChannel(outputStream)) {
Pipe pipe = Pipe.open();
//异步任务
CompletableFuture.runAsync(() -> runTask(pipe, files));
//获取读通道
try (ReadableByteChannel readableByteChannel = pipe.source()) {
ByteBuffer buffer = ByteBuffer.allocate(TEMP_SIZE);
while (readableByteChannel.read(buffer) >= 0) {
buffer.flip();
out.write(buffer);
buffer.clear();
}
}
}catch (Exception e){
e.printStackTrace();
}
}
private static void runTask(Pipe pipe, Map<String, String> files) {
try(ZipOutputStream zos = new ZipOutputStream(Channels.newOutputStream(pipe.sink()));
WritableByteChannel out = Channels.newChannel(zos)) {
for (Map.Entry<String, String> entry : files.entrySet()) {
taskFunction(zos, out, entry.getKey(), entry.getValue());
}
}catch (Exception e){
e.printStackTrace();
}
}
/**
* 打包文件
* @param zos 压缩包输出
* @param out 缓冲区通道
* @param fileName 文件名称
* @param file 文件
* @throws IOException IOException
*/
private static void taskFunction(ZipOutputStream zos, WritableByteChannel out, String fileName, File file) throws IOException {
// 是否为目录
if (file.isDirectory()) {
File[] files = file.listFiles();
fileName = StrUtil.isEmpty(fileName) ? file.getName() + "/" : fileName + "/";
if (files == null || files.length == 0){
return;
}
for (File child : files) {
taskFunction(zos, out, fileName + child.getName(), child);
}
} else {
fileName = StrUtil.isEmpty(fileName) ? file.getName() : fileName;
zos.putNextEntry(new ZipEntry(fileName));
try(FileInputStream fis = new FileInputStream(file.getAbsolutePath())){
FileChannel fileChannel = fis.getChannel();
fileChannel.transferTo(0, fileChannel.size(), out);
fileChannel.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
private static void taskFunction(ZipOutputStream zos, WritableByteChannel out, String fileName, String file) throws IOException {
// 网络文件
if (file.startsWith("http")) {
zos.putNextEntry(new ZipEntry(fileName));
byte[] bytes = HttpUtil.downloadBytes(file);
out.write(ByteBuffer.wrap(bytes));
}else {
taskFunction(zos, out, fileName, new File(file));
}
}
}

View File

@ -1,9 +1,15 @@
package org.dromara.email.core.service;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import org.dromara.email.api.MailClient;
import org.dromara.email.api.Parameter;
import org.dromara.email.comm.errors.MailException;
import org.dromara.email.comm.utils.HtmlUtil;
import org.dromara.email.comm.utils.ZipUtils;
import org.dromara.email.core.ReflectUtil;
import javax.activation.DataHandler;
@ -15,6 +21,10 @@ import javax.mail.Multipart;
import javax.mail.Transport;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMultipart;
import javax.mail.util.ByteArrayDataSource;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@ -22,7 +32,7 @@ import java.util.Objects;
public class MailService implements MailClient {
private MailBuild mailBuild;
private final MailBuild mailBuild;
private MailService(MailBuild mailBuild) {
this.mailBuild = mailBuild;
@ -47,13 +57,33 @@ public class MailService implements MailClient {
sendEmail(Collections.singletonList(mailAddress), title, body, files);
}
@Override
public void sendEmail(String mailAddress, String title, String body, String zipName, Map<String, String> files) {
try {
Message message = mailBuild.getMessage();
message.setRecipients(Message.RecipientType.TO, mailBuild.eliminate(Convert.toList(String.class, mailAddress)));
message.setSubject(title);
if (StrUtil.isNotBlank(body)) {
message.setText(body);
}
if (files != null && files.size() != 0) {
Multipart multipart = new MimeMultipart();
zipFiles(multipart, zipName, files);
message.setContent(multipart);
}
Transport.send(message);
} catch (MessagingException | IOException e) {
throw new MailException(e);
}
}
@Override
public void sendEmail(List<String> mailAddress, String title, String body, Map<String,String> files) {
try {
Message message = mailBuild.getMessage();
message.setRecipients(Message.RecipientType.TO, mailBuild.eliminate(mailAddress));
message.setSubject(title);
if (Objects.isNull(body) || body.isEmpty()) {
if (StrUtil.isNotBlank(body)) {
message.setText(body);
}
if (files != null && files.size() != 0) {
@ -132,6 +162,38 @@ public class MailService implements MailClient {
sendHtml(Collections.singletonList(mailAddress), title, body, htmlName, parameter, files);
}
@Override
public void sendHtml(String mailAddress, String title, String body, String htmlName, Map<String, String> parameter, String zipName, Map<String, String> files) {
try {
Message message = mailBuild.getMessage();
message.setRecipients(Message.RecipientType.TO, mailBuild.eliminate(Convert.toList(String.class, mailAddress)));
message.setSubject(title);
Multipart multipart = new MimeMultipart("alternative");
//读取模板并进行变量替换
List<String> strings = HtmlUtil.replacePlaceholder(HtmlUtil.readHtml(htmlName), parameter);
//拼合HTML数据
String htmlData = HtmlUtil.pieceHtml(strings);
if (StrUtil.isNotBlank(body)) {
// 创建文本正文部分
MimeBodyPart textPart = new MimeBodyPart();
textPart.setText(body);
multipart.addBodyPart(textPart);
}
//添加附件
if (MapUtil.isNotEmpty(files)) {
zipFiles(multipart, zipName, files);
}
MimeBodyPart htmlPart = new MimeBodyPart();
htmlPart.setContent(htmlData, "text/html;charset=UTF-8");
multipart.addBodyPart(htmlPart);
message.setContent(multipart);
Transport.send(message);
} catch (MessagingException | IOException e) {
throw new MailException(e);
}
}
@Override
public void sendHtml(List<String> mailAddress, String title, String body, String htmlName, Map<String, String> parameter, Map<String, String> files) {
try {
@ -186,4 +248,16 @@ public class MailService implements MailClient {
multipart.addBodyPart(messageBodyPart);
}
}
private void zipFiles(Multipart multipart, String zipName, Map<String, String> files) throws MessagingException, IOException {
// 设置附件消息部分
MimeBodyPart messageBodyPart = new MimeBodyPart();
ByteArrayOutputStream os = new ByteArrayOutputStream();
ZipUtils.zipFilePip(files, os);
ByteArrayInputStream stream = IoUtil.toStream(os);
DataSource source = new ByteArrayDataSource(stream, "application/octet-stream");
messageBodyPart.setDataHandler(new DataHandler(source));
messageBodyPart.setFileName(StrUtil.isNotBlank(zipName) ? zipName: UUID.fastUUID() + ".zip");
multipart.addBodyPart(messageBodyPart);
}
}