Встроенные изображения в электронной почте с помощью JavaMail
Я хочу отправить электронное письмо со встроенным изображением с помощью javamail.
Я делаю что-то вроде этого.
MimeMultipart content = new MimeMultipart("related");
BodyPart bodyPart = new MimeBodyPart();
bodyPart.setContent(message, "text/html; charset=ISO-8859-1");
content.addBodyPart(bodyPart);
bodyPart = new MimeBodyPart();
DataSource ds = new ByteArrayDataSource(image, "image/jpeg");
bodyPart.setDataHandler(new DataHandler(ds));
bodyPart.setHeader("Content-Type", "image/jpeg; name=image.jpg");
bodyPart.setHeader("Content-ID", "<image>");
bodyPart.setHeader("Content-Disposition", "inline");
content.addBodyPart(bodyPart);
msg.setContent(content);
Я также пробовал
bodyPart.setHeader("inline; filename=image.jpg");
и
bodyPart.setDisposition("inline");
но независимо от того, изображение отправляется как вложение, а Content-Dispostion превращается в "вложение".
Как отправить изображение в строку с помощью javamail?
Ответы
Ответ 1
Твоя проблема
Насколько я вижу, похоже, что вы создаете сообщение, и все в основном правильно! Вы используете правильные типы MIME и все.
Я не уверен, почему вы используете DataSource и DataHandler, и у вас есть ContentID на изображении, но вам нужно завершить свой вопрос, чтобы я мог больше устранять неполадки. Особенно, следующая строка:
bodyPart.setContent(message, "text/html; charset=ISO-8859-1");
Что в message
? Содержит ли он <img src="cid:image"/>
?
Вы пытались сгенерировать ContentID с помощью String cid = ContentIdGenerator.getContentId();
вместо использования image
Источник
Эта статья в блоге научила меня, как использовать правильный тип сообщения, прикрепить мое изображение и сослаться на вложение из тела HTML: Как отправить электронное письмо со встроенными изображениями с помощью Java
подробности
Сообщение
Вы должны создать свой контент, используя класс MimeMultipart
. Важно использовать строку "related"
в качестве параметра для конструктора, чтобы сообщить JavaMail, что ваши части "работают вместе".
MimeMultipart content = new MimeMultipart("related");
Идентификатор контента
Вам нужно сгенерировать ContentID, это строка, используемая для идентификации изображения, которое вы прикрепили к вашему письму, и ссылки на него из тела письма.
String cid = ContentIdGenerator.getContentId();
Примечание. Этот класс ContentIdGenerator
является гипотетическим. Вы можете создать один или встроить создание идентификаторов контента. В моем случае я использую простой метод:
import java.util.UUID;
// ...
String generateContentId(String prefix) {
return String.format("%s-%s", prefix, UUID.randomUUID());
}
Тело HTML
HTML-код является частью содержимого MimeMultipart
. Для этого используйте класс MimeBodyPart
. Не забудьте указать encoding
и "html"
при установке текста этой части!
MimeBodyPart htmlPart = new MimeBodyPart();
htmlPart.setText(""
+ "<html>"
+ " <body>"
+ " <p>Here is my image:</p>"
+ " <img src=\"cid:" + cid + "\" />"
+ " </body>"
+ "</html>"
,"US-ASCII", "html");
content.addBodyPart(htmlPart);
Обратите внимание, что в качестве источника изображения мы используем cid:
и сгенерированный ContentID.
Прикрепление изображения
Мы можем создать еще один MimeBodyPart
для прикрепления изображения.
MimeBodyPart imagePart = new MimeBodyPart();
imagePart.attachFile("resources/teapot.jpg");
imagePart.setContentID("<" + cid + ">");
imagePart.setDisposition(MimeBodyPart.INLINE);
content.addBodyPart(imagePart);
Обратите внимание, что мы используем тот же ContentID между <
и >
и устанавливаем его как ContentID изображения. Мы также установили расположение на INLINE
чтобы сигнализировать, что это изображение предназначено для отображения в электронном письме, а не в качестве вложения.
Завершить сообщение
Это! Если вы создадите SMTP-сообщение в нужный сеанс и будете использовать этот контент, ваша электронная почта будет содержать встроенное изображение! Например:
SMTPMessage m = new SMTPMessage(session);
m.setContent(content);
m.setSubject("Mail with embedded image");
m.setRecipient(RecipientType.TO, new InternetAddress("[email protected]"));
Transport.send(m)
Дайте мне знать, если это работает для вас! ;)
Ответ 2
Почему бы вам не попробовать что-то вроде этого?
MimeMessage mail = new MimeMessage(mailSession);
mail.setSubject(subject);
MimeBodyPart messageBodyPart = new MimeBodyPart();
messageBodyPart.setContent(message, "text/html");
Multipart multipart = new MimeMultipart();
multipart.addBodyPart(messageBodyPart);
messageBodyPart = new MimeBodyPart();
DataSource source = new FileDataSource(new File("complete path to image.jpg"));
messageBodyPart.setDataHandler(new DataHandler(source));
messageBodyPart.setFileName(fileAttachment.getName());
messageBodyPart.setDisposition(MimeBodyPart.INLINE);
multipart.addBodyPart(messageBodyPart);
mail.setContent(multipart);
в сообщении,
<img src="image.jpg"/>
, и вы должны быть в порядке.
Удача
Ответ 3
Это сработало для меня:
MimeMultipart rootContainer = new MimeMultipart();
rootContainer.setSubType("related");
rootContainer.addBodyPart(alternativeMultiPartWithPlainTextAndHtml); // not in focus here
rootContainer.addBodyPart(createInlineImagePart(base64EncodedImageContentByteArray));
...
message.setContent(rootContainer);
message.setHeader("MIME-Version", "1.0");
message.setHeader("Content-Type", rootContainer.getContentType());
...
BodyPart createInlineImagePart(byte[] base64EncodedImageContentByteArray) throws MessagingException {
InternetHeaders headers = new InternetHeaders();
headers.addHeader("Content-Type", "image/jpeg");
headers.addHeader("Content-Transfer-Encoding", "base64");
MimeBodyPart imagePart = new MimeBodyPart(headers, base64EncodedImageContentByteArray);
imagePart.setDisposition(MimeBodyPart.INLINE);
imagePart.setContentID("<image>");
imagePart.setFileName("image.jpg");
return imagePart;
Ответ 4
Если вы используете Spring, используйте MimeMessageHelper
для отправки электронной почты со встроенным содержимым (Ссылки).
Создайте компонент JavaMailSender или настройте его, добавив соответствующие свойства в файл application.properties, если вы используете Spring Boot.
@Bean
public JavaMailSender getJavaMailSender() {
JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
mailSender.setHost(host);
mailSender.setPort(port);
mailSender.setUsername(username);
mailSender.setPassword(password);
Properties props = mailSender.getJavaMailProperties();
props.put("mail.transport.protocol", "smtp");
props.put("mail.smtp.auth", authEnable);
props.put("mail.smtp.starttls.enable", starttlsEnable);
//props.put("mail.debug", "true");
mailSender.setJavaMailProperties(props);
return mailSender;
}
Создать алгоритм для генерации уникального CONTENT-ID
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Random;
public class ContentIdGenerator {
static int seq = 0;
static String hostname;
public static void getHostname() {
try {
hostname = InetAddress.getLocalHost().getCanonicalHostName();
}
catch (UnknownHostException e) {
// we can't find our hostname? okay, use something no one else is
// likely to use
hostname = new Random(System.currentTimeMillis()).nextInt(100000) + ".localhost";
}
}
/**
* Sequence goes from 0 to 100K, then starts up at 0 again. This is large
* enough,
* and saves
*
* @return
*/
public static synchronized int getSeq() {
return (seq++) % 100000;
}
/**
* One possible way to generate very-likely-unique content IDs.
*
* @return A content id that uses the hostname, the current time, and a
* sequence number
* to avoid collision.
*/
public static String getContentId() {
getHostname();
int c = getSeq();
return c + "." + System.currentTimeMillis() + "@" + hostname;
}
}
Отправить письмо со встроенными.
@Autowired
private JavaMailSender javaMailSender;
public void sendEmailWithInlineImage() {
MimeMessage mimeMessage = null;
try {
InternetAddress from = new InternetAddress(from, personal);
mimeMessage = javaMailSender.createMimeMessage();
mimeMessage.setSubject("Test Inline");
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
helper.setFrom(from);
helper.setTo("[email protected]");
String contentId = ContentIdGenerator.getContentId();
String htmlText = "Hello,</br> <p>This is test with email inlines.</p><img src=\"cid:" + contentId + "\" />";
helper.setText(htmlText, true);
ClassPathResource classPathResource = new ClassPathResource("static/images/first.png");
helper.addInline(contentId, classPathResource);
javaMailSender.send(mimeMessage);
}
catch (Exception e) {
LOGGER.error(e.getMessage());
}
}
Ответ 5
Ниже приведен полный код
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
private BodyPart createInlineImagePart() {
MimeBodyPart imagePart =null;
try
{
ByteArrayOutputStream baos=new ByteArrayOutputStream(10000);
BufferedImage img=ImageIO.read(new File(directory path,"sdf_email_logo.jpg"));
ImageIO.write(img, "jpg", baos);
baos.flush();
String base64String=Base64.encode(baos.toByteArray());
baos.close();
byte[] bytearray = Base64.decode(base64String);
InternetHeaders headers = new InternetHeaders();
headers.addHeader("Content-Type", "image/jpeg");
headers.addHeader("Content-Transfer-Encoding", "base64");
imagePart = new MimeBodyPart(headers, bytearray);
imagePart.setDisposition(MimeBodyPart.INLINE);
imagePart.setContentID("<sdf_email_logo>");
imagePart.setFileName("sdf_email_logo.jpg");
}
catch(Exception exp)
{
logError("17", "Logo Attach Error : "+exp);
}
return imagePart;
}
MimeMultipart mp = new MimeMultipart();
//mp.addBodyPart(createInlineImagePart());
mp.addBodyPart(createInlineImagePart());
String body="<img src=\"cid:sdf_email_logo\"/>"
Ответ 6
Используйте следующий фрагмент:
MimeBodyPart imgBodyPart = new MimeBodyPart();
imgBodyPart.attachFile("Image.png");
imgBodyPart.setContentID('<'+"[email protected]"+'>');
imgBodyPart.setDisposition(MimeBodyPart.INLINE);
imgBodyPart.setHeader("Content-Type", "image/png");
multipart.addBodyPart(imgBodyPart);
Вам не нужно кодировать в линию и базу - вы можете традиционно присоединяться и добавлять ссылку на текст основного сообщения, который имеет тип text/html
.
Однако не забудьте установить заголовок imgBodyPart
Content-Type
на image/jpg
или так прямо перед добавлением к основному сообщению (после того, как вы присоедините файл).
Ответ 7
У меня были некоторые проблемы с отображением встроенных изображений в GMail и Thunderbird, я провел несколько тестов и решил следующий (пример) код:
String imagePath = "/path/to/the/image.png";
String fileName = imagePath.substring(path.lastIndexOf('/') + 1);
String htmlText = "<html><body>TEST:<img src=\"cid:img1\"></body></html>";
MimeMultipart multipart = new MimeMultipart("related");
BodyPart messageBodyPart = new MimeBodyPart();
messageBodyPart.setContent(htmlText, "text/html; charset=utf-8");
multipart.addBodyPart(messageBodyPart);
messageBodyPart = new MimeBodyPart();
DataSource fds = new FileDataSource(imagePath);
messageBodyPart.setDataHandler(new DataHandler(fds));
messageBodyPart.setHeader("Content-ID", "<img1>");
messageBodyPart.setDisposition(MimeBodyPart.INLINE);
messageBodyPart.setFileName(fileName);
multipart.addBodyPart(messageBodyPart);
message.setContent(multipart);
Просто некоторые вещи, на которые стоит обратить внимание:
- "Content-ID" должен быть построен, как указано в RFC (https://tools.ietf.org/html/rfc2392), поэтому он должен быть частью атрибута src тега img, следующего за "cid: ", заключенный в угловые скобки (" <"и"> ")
- Я должен был установить имя файла
- нет необходимости в ширине, высоте, alt или заголовке в теге img
- Я должен был поставить кодировку таким образом, потому что тот в HTML игнорировался
Это помогло мне сделать отображение встроенных изображений для некоторых клиентов и в веб-клиенте GMail, я не имею в виду, что это будет работать везде и всегда! :)
(Извините за мой английский и за мои опечатки!)
Ответ 8
Спецификацию RFC можно найти здесь (https://tools.ietf.org/html/rfc2392).
Во-первых, HTML-контент электронной почты должен быть отформатирован следующим образом: "cid: logo.png" при использовании встроенных изображений, см.
<td style="width:114px;padding-top: 19px">
<img src="cid:logo.png" />
</td>
Во-вторых, объект MimeBodyPart должен установить свойство "disposition" как MimeBodyPart.INLINE, как показано ниже:
String filename = "logo.png"
BodyPart image = new MimeBodyPart();
image.setDisposition(MimeBodyPart.INLINE);
image.setFileName(filename);
image.setHeader("Content-ID", "<" +filename+">");
Имейте в виду, свойство Content-ID должно иметь префикс и суффикс с "<" и ">" в перспективе, а значение off имя файла должно совпадать с содержимым тега src img без префикса "cid:"
Наконец весь код ниже:
Message msg = new MimeMessage(session);
msg.setFrom(new InternetAddress("[email protected]");
InternetAddress[] recipients = { "[email protected]"};
msg.setRecipients(Message.RecipientType.TO, recipients);
msg.setSubject("for test");
msg.setSentDate(new Date());
BodyPart content = new MimeBodyPart();
content.setContent(<html><body> <img src="cid:logo.png" /> </body></html>, "text/html; charset=utf-8");
String fileName = "logo.png";
BodyPart image = new MimeBodyPart();
image.setHeader("Content-ID", "<" +fileName+">");
image.setDisposition(MimeBodyPart.INLINE);
image.setFileName(fileName);
InputStream stream = MailService.class.getResourceAsStream(path);
DataSource fds = new ByteArrayDataSource(IOUtils.toByteArray(stream), "image/png");
image.setDataHandler(new DataHandler(fds));
MimeMultipart multipart = new MimeMultipart("related");
multipart.addBodyPart(content);
multipart.addBodyPart(getImage(image1));
msg.setContent(multipart);
msg.saveChanges();
Transport bus = session.getTransport("smtp");
bus.connect("username", "password");
bus.sendMessage(msg, recipients);
bus.close();
Ответ 9
messageBodyPart.setContent(htmlText, "text/html"); multipart.addBodyPart(messageBodyPart);
messageBodyPart = new MimeBodyPart();
DataSource fds = new FileDataSource("resources/Images/bday.jpg");
messageBodyPart.setDataHandler(new DataHandler(fds));
messageBodyPart.setHeader("Content-ID", "<image>");
// add image to the multipart
multipart.addBodyPart(messageBodyPart);
msg.setContent(multipart);
Transport.send(msg);