support image

This commit is contained in:
Looly 2019-08-20 17:13:52 +08:00
parent b791a75dd6
commit 8a5efb84da
4 changed files with 143 additions and 83 deletions

View File

@ -7,7 +7,7 @@
### 新特性
* 【core】 改进CollUtil.zip逻辑减少内存复制issue#I10T01@Gitee
* 【extra】 邮件支持图片pr#495@Github
* 【extra】 邮件增加图片支持pr#495@Github
### Bug修复
* 【http】 修复HttpRquest中body方法长度计算问题issue#I10UPG@Gitee

View File

@ -5,13 +5,12 @@ import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.Date;
import java.util.Map;
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.activation.FileDataSource;
import javax.activation.FileTypeMap;
import javax.mail.Authenticator;
import javax.mail.BodyPart;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Session;
@ -21,8 +20,11 @@ import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.util.ByteArrayDataSource;
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;
/**
@ -49,10 +51,8 @@ public class Mail {
private String content;
/** 是否为HTML */
private boolean isHtml;
/** 附件列表 */
private DataSource[] attachments;
/** 图片列表 */
private Map<String, InputStream> imageMap;
/** 正文、附件和图片的混合部分 */
private Multipart multipart = new MimeMultipart();
/** 是否使用全局会话默认为false */
private boolean useGlobalSession = false;
@ -165,7 +165,8 @@ public class Mail {
}
/**
* 设置正文
* 设置正文<br>
* 正文可以是普通文本也可以是HTML默认普通文本可以通过调用{@link #setHtml(boolean)} 设置是否为HTML
*
* @param content 正文
* @return this
@ -185,9 +186,21 @@ public class Mail {
this.isHtml = isHtml;
return this;
}
/**
* 设置正文
*
* @param content 正文内容
* @param isHtml 是否为HTML
* @return this
*/
public Mail setContent(String content, boolean isHtml) {
setContent(content);
return setHtml(isHtml);
}
/**
* 设置文件类型附件
* 设置文件类型附件文件可以是图片文件此时自动设置cid正文中引用图片默认cid为文件名
*
* @param files 附件文件列表
* @return this
@ -205,7 +218,7 @@ public class Mail {
}
/**
* 设置附件附件使用{@link DataSource} 形式表示可以使用{@link FileDataSource}包装文件表示文件附件
* 增加附件或图片附件使用{@link DataSource} 形式表示可以使用{@link FileDataSource}包装文件表示文件附件
*
* @param attachments 附件列表
* @return this
@ -213,21 +226,74 @@ public class Mail {
*/
public Mail setAttachments(DataSource... attachments) {
if (ArrayUtil.isNotEmpty(attachments)) {
this.attachments = 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 = InternalMailUtil.encodeText(attachment.getName(), 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 图片与占位符占位符格式为cid:${cid}
* @param imageStream 图片文件
* @since 4.6.3
*/
public Mail addImage(String cid, InputStream imageStream) {
return addImage(cid, imageStream, null);
}
/**
* 增加图片图片的键对应到邮件模板中的占位字符串
*
* @param cid 图片与占位符占位符格式为cid:${cid}
* @param imageStream 图片流不关闭
* @param contentType 图片类型null赋值默认的"image/jpeg"
* @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 imageMap 图片与占位符占位符格式为cid:$IMAGE_PLACEHOLDER
* @param cid 图片与占位符占位符格式为cid:${cid}
* @param imageFile 图片文件
* @since 4.6.3
*/
public Mail setImageMap(Map<String, InputStream> imageMap) {
if (imageMap != null && imageMap.size() > 0) {
this.imageMap = imageMap;
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);
}
return this;
}
/**
@ -330,43 +396,12 @@ public class Mail {
* @throws MessagingException 消息异常
*/
private Multipart buildContent(Charset charset) throws MessagingException {
final Multipart mainPart = new MimeMultipart();
// 正文
final BodyPart body = new MimeBodyPart();
final MimeBodyPart body = new MimeBodyPart();
body.setContent(content, StrUtil.format("text/{}; charset={}", isHtml ? "html" : "plain", charset));
mainPart.addBodyPart(body);
this.multipart.addBodyPart(body);
// 附件
if (ArrayUtil.isNotEmpty(this.attachments)) {
BodyPart bodyPart;
for (DataSource attachment : attachments) {
bodyPart = new MimeBodyPart();
bodyPart.setDataHandler(new DataHandler(attachment));
bodyPart.setFileName(InternalMailUtil.encodeText(attachment.getName(), charset));
mainPart.addBodyPart(bodyPart);
}
}
// 图片
for (Map.Entry<String, InputStream> entry : imageMap.entrySet()) {
MimeBodyPart imgBodyPart = new MimeBodyPart();
DataSource ds;
try {
ds = new ByteArrayDataSource(entry.getValue(), "image/jpeg");
IoUtil.close(entry.getValue());
} catch (IOException e) {
throw new MailException(e);
}
imgBodyPart.setDataHandler(new DataHandler(ds));
// imgBodyPart.setHeader("Content-ID", String.format("<%s>", entry.getKey()));
imgBodyPart.setContentID(entry.getKey());
// add it
mainPart.addBodyPart(imgBodyPart);
}
return mainPart;
return this.multipart;
}
/**

View File

@ -5,8 +5,11 @@ import java.io.InputStream;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
/**
@ -129,7 +132,7 @@ public class MailUtil {
send(GlobalMailAccount.INSTANCE.getAccount(), true, tos, ccs, bccs, subject, content, null, isHtml, files);
}
//------------------------------------------------------------------------------------------------------------------------------- Custom MailAccount
// ------------------------------------------------------------------------------------------------------------------------------- Custom MailAccount
/**
* 发送邮件给多人
*
@ -175,7 +178,7 @@ public class MailUtil {
public static void send(MailAccount mailAccount, Collection<String> tos, Collection<String> ccs, Collection<String> bccs, String subject, String content, boolean isHtml, File... files) {
send(mailAccount, false, tos, ccs, bccs, subject, content, null, isHtml, files);
}
/**
* 使用配置文件中设置的账户发送HTML邮件发送给单个或多个收件人<br>
* 多个收件人可以使用逗号,分隔也可以通过分号;分隔
@ -205,7 +208,7 @@ public class MailUtil {
public static void send(String to, String subject, String content, Map<String, InputStream> imageMap, boolean isHtml, File... files) {
send(splitAddress(to), subject, content, imageMap, isHtml, files);
}
/**
* 使用配置文件中设置的账户发送邮件发送单个或多个收件人<br>
* 多个收件人抄送人密送人可以使用逗号,分隔也可以通过分号;分隔
@ -223,7 +226,7 @@ public class MailUtil {
public static void send(String to, String cc, String bcc, String subject, String content, Map<String, InputStream> imageMap, boolean isHtml, File... files) {
send(splitAddress(to), splitAddress(cc), splitAddress(bcc), subject, content, imageMap, isHtml, files);
}
/**
* 使用配置文件中设置的账户发送HTML邮件发送给多人
*
@ -251,7 +254,7 @@ public class MailUtil {
public static void send(Collection<String> tos, String subject, String content, Map<String, InputStream> imageMap, boolean isHtml, File... files) {
send(tos, null, null, subject, content, imageMap, isHtml, files);
}
/**
* 使用配置文件中设置的账户发送邮件发送给多人
*
@ -268,8 +271,8 @@ public class MailUtil {
public static void send(Collection<String> tos, Collection<String> ccs, Collection<String> bccs, String subject, String content, Map<String, InputStream> imageMap, boolean isHtml, File... files) {
send(GlobalMailAccount.INSTANCE.getAccount(), true, tos, ccs, bccs, subject, content, imageMap, isHtml, files);
}
//------------------------------------------------------------------------------------------------------------------------------- Custom MailAccount
// ------------------------------------------------------------------------------------------------------------------------------- Custom MailAccount
/**
* 发送邮件给多人
*
@ -296,11 +299,12 @@ public class MailUtil {
* @param imageMap 图片与占位符占位符格式为cid:$IMAGE_PLACEHOLDER
* @param isHtml 是否为HTML格式
* @param files 附件列表
* @since 4.6.3
*/
public static void send(MailAccount mailAccount, Collection<String> tos, String subject, String content, Map<String, InputStream> imageMap, boolean isHtml, File... files) {
send(mailAccount, tos, null, null, subject, content, imageMap, isHtml, files);
}
/**
* 发送邮件给多人
*
@ -313,13 +317,14 @@ public class MailUtil {
* @param imageMap 图片与占位符占位符格式为cid:$IMAGE_PLACEHOLDER
* @param isHtml 是否为HTML格式
* @param files 附件列表
* @since 4.0.3
* @since 4.6.3
*/
public static void send(MailAccount mailAccount, Collection<String> tos, Collection<String> ccs, Collection<String> bccs, String subject, String content, Map<String, InputStream> imageMap, boolean isHtml, File... files) {
public static void send(MailAccount mailAccount, Collection<String> tos, Collection<String> ccs, Collection<String> bccs, String subject, String content, Map<String, InputStream> imageMap,
boolean isHtml, File... files) {
send(mailAccount, false, tos, ccs, bccs, subject, content, imageMap, isHtml, files);
}
//------------------------------------------------------------------------------------------------------------------------ Private method start
// ------------------------------------------------------------------------------------------------------------------------ Private method start
/**
* 发送邮件给多人
*
@ -330,53 +335,62 @@ public class MailUtil {
* @param bccs 密送人列表可以为null或空
* @param subject 标题
* @param content 正文
* @param imageMap 图片与占位符占位符格式为cid:$IMAGE_PLACEHOLDER
* @param imageMap 图片与占位符占位符格式为cid:${cid}
* @param isHtml 是否为HTML格式
* @param files 附件列表
* @since 4.0.3
* @since 4.6.3
*/
private static void send(MailAccount mailAccount, boolean useGlobalSession, Collection<String> tos, Collection<String> ccs, Collection<String> bccs, String subject, String content, Map<String, InputStream> imageMap, boolean isHtml, File... files) {
private static void send(MailAccount mailAccount, boolean useGlobalSession, Collection<String> tos, Collection<String> ccs, Collection<String> bccs, String subject, String content,
Map<String, InputStream> imageMap, boolean isHtml, File... files) {
final Mail mail = Mail.create(mailAccount).setUseGlobalSession(useGlobalSession);
//可选抄送人
if(CollUtil.isNotEmpty(ccs)) {
// 可选抄送人
if (CollUtil.isNotEmpty(ccs)) {
mail.setCcs(ccs.toArray(new String[ccs.size()]));
}
//可选密送人
if(CollUtil.isNotEmpty(bccs)) {
// 可选密送人
if (CollUtil.isNotEmpty(bccs)) {
mail.setBccs(bccs.toArray(new String[bccs.size()]));
}
mail.setTos(tos.toArray(new String[tos.size()]));
mail.setTitle(subject);
mail.setContent(content);
mail.setHtml(isHtml);
mail.setFiles(files);
mail.setImageMap(imageMap);
// 图片
if(MapUtil.isNotEmpty(imageMap)) {
for (Entry<String, InputStream> entry : imageMap.entrySet()) {
mail.addImage(entry.getKey(), entry.getValue());
// 关闭流
IoUtil.close(entry.getValue());
}
}
mail.send();
}
/**
* 将多个联系人转为列表分隔符为逗号或者分号
*
* @param addresses 多个联系人如果为空返回null
* @return 联系人列表
*/
private static List<String> splitAddress(String addresses){
if(StrUtil.isBlank(addresses)) {
private static List<String> splitAddress(String addresses) {
if (StrUtil.isBlank(addresses)) {
return null;
}
List<String> result;
if(StrUtil.contains(addresses, ',')) {
if (StrUtil.contains(addresses, ',')) {
result = StrUtil.splitTrim(addresses, ',');
}else if(StrUtil.contains(addresses, ';')) {
} else if (StrUtil.contains(addresses, ';')) {
result = StrUtil.splitTrim(addresses, ';');
}else {
} else {
result = CollUtil.newArrayList(addresses);
}
return result;
}
//------------------------------------------------------------------------------------------------------------------------ Private method end
// ------------------------------------------------------------------------------------------------------------------------ Private method end
}

View File

@ -1,5 +1,8 @@
package cn.hutool.extra.mail;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import org.junit.Assert;
@ -17,17 +20,25 @@ public class MailTest {
@Test
@Ignore
public void sendTest() {
public void sendWithFileTest() {
MailUtil.send("hutool@foxmail.com", "测试", "<h1>邮件来自Hutool测试</h1>", true, FileUtil.file("d:/测试附件文本.txt"));
}
@Test
@Ignore
public void sendTest2() {
public void sendWithLongNameFileTest() {
//附件名长度大于60时的测试
MailUtil.send("hutool@foxmail.com", "测试", "<h1>邮件来自Hutool测试</h1>", true, FileUtil.file("d:/6-LongLong一阶段平台建设周报2018.3.12-3.16.xlsx"));
}
@Test
@Ignore
public void sendWithImageTest() {
Map<String, InputStream> map = new HashMap<>();
map.put("testImage", FileUtil.getInputStream("f:/test/me.png"));
MailUtil.sendHtml("hutool@foxmail.com", "测试", "<h1>邮件来自Hutool测试</h1><img src=\"cid:testImage\" />", map);
}
@Test
@Ignore
public void sendHtmlTest() {