I am having a really weird problem when adding more than one MimeBodyPart to a MimeMultiPart. Essentially, if the MimeMultiPart is mixed, then all MimeBodyPart objects are added as attachments, except for the last one, which is added to the body. If the MimeMultiPart is alternative, then only the last MimeBodyPart is added to the body.
I am just trying to write a simple email which should have an opening (text), followed by a table (html), and finally a closing (text).
Code
Here is the code for my function creating the email. It generates the MimeMessage and then opens it in Outlook as a draft.
public void writeEmail() throws Exception {
//Create message envelope.
MimeMessage msg = new MimeMessage((Session) null);
//Set destination and cc
msg.addFrom(InternetAddress.parse("SELECT#ACCOUNT"));
msg.setRecipients(Message.RecipientType.TO, InternetAddress.parse("support#bar.com"));
msg.setRecipients(Message.RecipientType.CC, InternetAddress.parse("manager#baz.com"));
//Create and set the subject
String subject = "subject";
msg.setSubject(subject);
//Make the message as a draft
msg.setHeader("X-Unsent", "1");
//Create the body
MimeMultipart mmp = new MimeMultipart("alternative");
//Tests
String opening = "Opening";
String html = "<h1>" + table + "</h1>";
String closing = "Closing";
MimeBodyPart openingPart = new MimeBodyPart();
openingPart.setDisposition(MimeBodyPart.INLINE);
openingPart.setText(opening, "utf-8");
MimeBodyPart htmlPart = new MimeBodyPart();
htmlPart.setDisposition(MimeBodyPart.INLINE);
htmlPart.setContent(html, "text/html; charset=utf-8");
MimeBodyPart closingPart = new MimeBodyPart();
closingPart.setDisposition(MimeBodyPart.INLINE);
closingPart.setText(closing, "utf-8");
mmp.addBodyPart(openingPart);
mmp.addBodyPart(htmlPart);
mmp.addBodyPart(closingPart);
msg.setContent(mmp);
msg.saveChanges();
//Save as a temporary file
File resultEmail = File.createTempFile("email", ".eml");
try (FileOutputStream fs = new FileOutputStream(resultEmail)) {
msg.writeTo(fs);
fs.flush();
fs.getFD().sync();
}
System.out.println(resultEmail.getCanonicalPath());
ProcessBuilder pb = new ProcessBuilder();
pb.command("cmd.exe", "/C", "start", "outlook.exe", "/eml", resultEmail.getCanonicalPath());
Process p = pb.start();
try {
p.waitFor();
} finally {
p.getErrorStream().close();
p.getInputStream().close();
p.getErrorStream().close();
p.destroy();
}
}
Question
How can I render all the MimeBodyPart as text inside of the email body, instead of them being wrongfully added as attachments?
Related
I'm struggling to send a csv file over javamail.
Since the content is small, I constructed manually the data as CSV format, stored in memory and passed as ByteArrayDataSource to MimeMessageHelper. However, the received file has strangely double the content.
The code is pretty standard:
// mail sender
try {
MimeMessage mail = javaMailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mail, true, "UTF-8");
helper.setSubject(mailObj.getMailSubject());
helper.setFrom(mailObj.getMailFrom());
helper.setTo(mailObj.getMailTo());
Mail.Attachment attachment = mailObj.getAttachment();
if (attachment != null) helper.addAttachment(attachment.getFilename(),
attachment.getDataSource());
javaMailSender.send(helper.getMimeMessage());
} catch (Exception e) {
log.error("Cannot send email " + mailObj.toString(), e);
}
DataSource createDataSource(Data originalData) throws IOException {
try (StringWriter out = new StringWriter(); PrintWriter pw = new PrintWriter(out) {
#Override
public void println() {
write(LINE_SEP);
}
}) {
pw.write('\uFEFF'); // Write BOM
pw.println(HEADERS);
for (AppointmentBookingEvent details : appointments) {
// Concatenate the data with PrintWriter.println(...);
}
return new ByteArrayDataSource(out.toString().getBytes(StandardCharsets.UTF_8), "text/csv");
}
}
I noticed that the method DataSource.getInputStream() is called twice by the sender's internal functions. Was that by any chance the cause ?
It seems the problem came from MimeMessageHelper. I could not figure exactly where but the problem has gone when I explicitly constructed the body parts of mail.
MimeMessage mail = javaMailSender.createMimeMessage();
mail.setFrom(new InternetAddress(mailObj.getMailFrom()));
mail.setRecipients(Message.RecipientType.TO, InternetAddress.parse(mailObj.getMailTo()));
mail.setSubject(mailObj.getMailSubject());
Multipart multipart = new MimeMultipart();
BodyPart messageBodyPart = new MimeBodyPart();
messageBodyPart.setContent(getContentFromTemplate(mailObj.getModel(), mailObj.getLang(), mailObj.getTemplateName()), "text/html; charset=UTF-8");
multipart.addBodyPart(messageBodyPart);
Mail.Attachment attachment = mailObj.getAttachment();
if (attachment != null) {
BodyPart attachmentBodyPart = new MimeBodyPart();
attachmentBodyPart.setFileName(attachment.getFilename());
attachmentBodyPart.setDisposition(Part.ATTACHMENT);
attachmentBodyPart.setDataHandler(new DataHandler(attachment.getDataSource()));
multipart.addBodyPart(attachmentBodyPart);
}
mail.setContent(multipart);
javaMailSender.send(mail);
I am trying to send email with created pdf document as the attachment, the environment i am working is the REST based java spring boot application,
Actually i know how to send email with the thymeleaf template engine, but how can i create a pdf document in memory, and send it as a attachment, this is the code that i am using for sending email.
Context cxt = new Context();
cxt.setVariable("doctorFullName", doctorFullName);
String message = templateEngine.process(mailTemplate, cxt);
emailService.sendMail(MAIL_FROM, TO_EMAIL,"Subject", "message");
and this this the sendmail() function
#Override
public void sendPdfMail(String fromEmail, String recipientMailId, String subject, String body) {
logger.info("--in the function of sendMail");
final MimeMessage mimeMessage = this.mailSender.createMimeMessage();
try {
final MimeMessageHelper message = new MimeMessageHelper(mimeMessage, true, "UTF-8");
message.setSubject(subject);
message.setFrom(fromEmail);
message.setTo(recipientMailId);
message.setText(body, true);
FileSystemResource file = new FileSystemResource("C:\\xampp\\htdocs\\project-name\\logs\\health360-logging.log");
message.addAttachment(file.getFilename(), file);
this.mailSender.send(mimeMessage);
logger.info("--Mail Sent Successfully");
} catch (MessagingException e) {
logger.info("--Mail Sent failed ---> " + e.getMessage());
throw new RuntimeException(e.getMessage());
}
}
Actually i need to create a kind of report with 2-3 pages as pdf file, and send in mail.
Also i need to send multiple pdf reports, in the mail, how can i do this, can you friends help me on this, i found something called jasper, is it something related to my environment,
This is how you can send a mail:
public void email() {
String smtpHost = "yourhost.com"; //replace this with a valid host
int smtpPort = 587; //replace this with a valid port
String sender = "sender#yourhost.com"; //replace this with a valid sender email address
String recipient = "recipient#anotherhost.com"; //replace this with a valid recipient email address
String content = "dummy content"; //this will be the text of the email
String subject = "dummy subject"; //this will be the subject of the email
Properties properties = new Properties();
properties.put("mail.smtp.host", smtpHost);
properties.put("mail.smtp.port", smtpPort);
Session session = Session.getDefaultInstance(properties, null);
ByteArrayOutputStream outputStream = null;
try {
//construct the text body part
MimeBodyPart textBodyPart = new MimeBodyPart();
textBodyPart.setText(content);
//now write the PDF content to the output stream
outputStream = new ByteArrayOutputStream();
writePdf(outputStream);
byte[] bytes = outputStream.toByteArray();
//construct the pdf body part
DataSource dataSource = new ByteArrayDataSource(bytes, "application/pdf");
MimeBodyPart pdfBodyPart = new MimeBodyPart();
pdfBodyPart.setDataHandler(new DataHandler(dataSource));
pdfBodyPart.setFileName("test.pdf");
//construct the mime multi part
MimeMultipart mimeMultipart = new MimeMultipart();
mimeMultipart.addBodyPart(textBodyPart);
mimeMultipart.addBodyPart(pdfBodyPart);
//create the sender/recipient addresses
InternetAddress iaSender = new InternetAddress(sender);
InternetAddress iaRecipient = new InternetAddress(recipient);
//construct the mime message
MimeMessage mimeMessage = new MimeMessage(session);
mimeMessage.setSender(iaSender);
mimeMessage.setSubject(subject);
mimeMessage.setRecipient(Message.RecipientType.TO, iaRecipient);
mimeMessage.setContent(mimeMultipart);
//send off the email
Transport.send(mimeMessage);
System.out.println("sent from " + sender +
", to " + recipient +
"; server = " + smtpHost + ", port = " + smtpPort);
} catch(Exception ex) {
ex.printStackTrace();
} finally {
//clean off
if(null != outputStream) {
try { outputStream.close(); outputStream = null; }
catch(Exception ex) { }
}
}
}
You can see that we create a MimeBodyPart with a DataSource created from bytes that resulted from a method named writePdf():
public void writePdf(OutputStream outputStream) throws Exception {
Document document = new Document();
PdfWriter.getInstance(document, outputStream);
document.open();
Paragraph paragraph = new Paragraph();
paragraph.add(new Chunk("hello!"));
document.add(paragraph);
document.close();
}
Since we use a ByteOutputStream instead of a FileOutputStream no file is written to disk.
My below Java class has to copy a text file content and mail to recipients, along with a attached xls file,using java mail.
Now I can read and Mail the text file content but unable to attach the xls file.
Below are my snippet:
static void sendmail() throws IOException,
MessagingException,AddressException,FileNotFoundException
{
DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy
HH:mm:ss");
Calendar cal = Calendar.getInstance();
String to1=getOrderinfo.to1;
String to2 = getOrderinfo.to2;
String to3 = getOrderinfo.to3;
String to4 = getOrderinfo.to4;
String from =getOrderinfo.from;
String host = getOrderinfo.host;
Properties properties = System.getProperties();
properties.setProperty("mail.smtp.host", host);
Session session = Session.getDefaultInstance(properties);
MimeMessage message = new MimeMessage(session);
Multipart multipart = new MimeMultipart();
String pathLogFile = "E:/car_failed_report/log.txt";
try {
message.setFrom(new InternetAddress(from));
message.addRecipient(Message.RecipientType.TO, new
InternetAddress(to1));
message.setSubject(" CAR NOT YET INTEGRATED REPORT at
: "+dateFormat.format(cal.getTime()));
StringBuffer sb = new StringBuffer();
FileInputStream fstream = new
FileInputStream(pathLogFile);
BufferedReader br = new BufferedReader(new
InputStreamReader(fstream));
String singleLine;
while ((singleLine = br.readLine()) != null)
{
sb.append(singleLine + "<br>");
// CarParser1.sb1.append(singleLine +"<br>");
}
br.close();
String allLines;
allLines = sb.toString();
String allLines_html=" <html><head><title></title>
</head>"
+ "<body >"+allLines+"</body ></html>";
message.setContent(allLines_html, "text/html;
charset=ISO-8859-1");
MimeBodyPart attachPart = new MimeBodyPart();
File attachement = new File("E:\\car_failed_report
\\failed.xls");
if (attachement.exists()) {
attachPart.attachFile(attachement);
} else {
System.out.println("ERROR READING THE FILE");
throw new FileNotFoundException();
}
Transport.send(message);
System.out.println("Email Sent successfully....");
System.out.println();
}
catch(FileNotFoundException f1)
{
System.out.println("File not yet created..this is from
mailer class");
return;
}
catch (MessagingException mex)
{
System.out.println("Invalid Email Address.please provide
a valid email id to send with");
mex.printStackTrace();
}
Can any one Help me to attach the xls file.?
Thanks in advance.
You created the body part with the attachment but didn't add it to the message. Replace your code starting with message.setContent:
MimeMultipart mp = new MimeMultipart();
MimeBodyPart body = new MimeBodyPart();
body.setText(allLines_html, "iso-8859-1", "html");
mp.addBodyPart(body);
MimeBodyPart attachPart = new MimeBodyPart();
File attachement = new File("E:\\car_failed_report\\failed.xls");
if (attachement.exists()) {
attachPart.attachFile(attachement);
mp.addBodyPart(attachPart);
} else {
System.out.println("ERROR READING THE FILE");
throw new FileNotFoundException();
}
message.setContent(mp);
I would like to create a MimeMessage which must have two parts as shown below picture (Part_0 and Part_2)
I'm trying to use below code to generated s/mime
public static void main(String[] a) throws Exception {
// create some properties and get the default Session
Properties props = new Properties();
// props.put("mail.smtp.host", host);
Session session = Session.getInstance(props, null);
// session.setDebug(debug);
MimeMessage msg = new MimeMessage(session);
try {
msg.addHeader("Content-Type", "multipart/signed; protocol=\"application/pkcs7-signature;");
msg.setFrom(new InternetAddress(FROM_EMAIL));
InternetAddress[] address = {new InternetAddress(
TO_EMAIL)};
msg.setRecipients(Message.RecipientType.TO, address);
msg.setSubject("Test Subject");
msg.setSentDate(new Date());
// create and fill the first message part
MimeBodyPart bodyPart1 = new MimeBodyPart();
bodyPart1.setHeader("Content-Type", "text/html; charset=utf-8");
bodyPart1.setContent("<b>Hello World</b>", "text/html");
Multipart multiPart = new MimeMultipart();
multiPart.addBodyPart(bodyPart1, 0);
try (OutputStream str = Files.newOutputStream(Paths
.get(UNSIGNED_MIME))) {
bodyPart1.writeTo(str);
}
signMime();
MimeBodyPart attachPart = new MimeBodyPart();
String filename = SIGNED_VALUE;
DataSource source = new FileDataSource(filename);
attachPart.setDataHandler(new DataHandler(source));
attachPart.setFileName("smime.p7s");
attachPart.addHeader("Content-Type", "application/pkcs7-signature; name=smime.p7s;");
attachPart.addHeader("Content-Transfer-Encoding", "base64");
attachPart.addHeader("Content-Description", "S/MIME Cryptographic Signature");
multiPart.addBodyPart(attachPart);
msg.setContent(multiPart, "multipart/signed; protocol=\"application/pkcs7-signature\"; ");
msg.saveChanges();
try (OutputStream str = Files.newOutputStream(Paths
.get(SIGNED_MIME))) {
msg.writeTo(str);
}
} catch (MessagingException mex) {
mex.printStackTrace();
Exception ex = null;
if ((ex = mex.getNextException()) != null) {
ex.printStackTrace();
}
}
I used two MimeBodyPart however I always got one Part_0 and generated eml file shown below.
I haven't tried to compile it, but what you want is something like this. The inner multipart is a body part of the outer multipart.
msg.setFrom(new InternetAddress(FROM_EMAIL));
InternetAddress[] address = {new InternetAddress(
TO_EMAIL)};
msg.setRecipients(Message.RecipientType.TO, address);
msg.setSubject("Test Subject");
msg.setSentDate(new Date());
MultipartSigned multiSigned = new MultipartSigned();
// create and fill the first message part
MimeBodyPart bodyPart1 = new MimeBodyPart();
bodyPart1.setText("<b>Hello World</b>", "utf-8", "html");
Multipart multiPart = new MimeMultipart();
multiPart.addBodyPart(bodyPart1);
// add other content to the inner multipart here
MimeBodyPart body = new MimeBodyPart();
body.setContent(multiPart);
multiSigned.addBodyPart(body);
try (OutputStream str = Files.newOutputStream(Paths
.get(UNSIGNED_MIME))) {
body.writeTo(str);
}
signMime();
MimeBodyPart attachPart = new MimeBodyPart();
String filename = SIGNED_VALUE;
attachPart.attachFile(filename,
"application/pkcs7-signature; name=smime.p7s", "base64");
attachPart.setFileName("smime.p7s");
attachPart.addHeader("Content-Description",
"S/MIME Cryptographic Signature");
multiSigned.addBodyPart(attachPart);
msg.setContent(multiSigned);
msg.saveChanges();
And you'll need this:
public class MultipartSigned extends MimeMultipart {
public MultipartSigned() {
super("signed");
ContentType ct = new ContentType(contentType);
ct.setParameter("protocol", "application/pkcs7-signature");
contentType = ct.toString();
}
}
You could make this much cleaner by moving more of the functionality into the MultipartSigned class.
What you are looking for is Spring Mime Helper.
I am trying to send an HTML email in the body of an email along with few file attachments as well. I came up with below code:
public void sendEmail(final String to, final String from, final String cc, final String subject, final String body,
final String baseDirectory, final List<String> listOfFileNames) {
for (int i = 1; i <= 3; i++) { // retrying
try {
Session session = Session.getInstance(mailProperties, null);
Multipart multipart = new MimeMultipart();
Message message = new MimeMessage(session);
message.setFrom(new InternetAddress(from));
InternetAddress[] toAddress = InternetAddress.parse(to);
InternetAddress[] ccAddress = InternetAddress.parse(cc);
message.addRecipients(RecipientType.TO, toAddress);
message.addRecipients(RecipientType.CC, ccAddress);
message.setSubject(subject);
message.setContent(body, "text/html;charset=utf8");
for (String file : listOfFileNames) {
String fileLocation = baseDirectory + "/" + file;
addAttachment(multipart, fileLocation);
}
message.setContent(multipart);
Transport.send(message, toAddress);
break;
} catch (Exception ex) {
// log exception
}
}
}
// this is used for attachment
private void addAttachment(final Multipart multipart, final String filename) throws MessagingException {
DataSource source = new FileDataSource(filename);
BodyPart messageBodyPart = new MimeBodyPart();
messageBodyPart.setDataHandler(new DataHandler(source));
messageBodyPart.setFileName(filename);
multipart.addBodyPart(messageBodyPart);
}
I need to have body String as part of my HTML email and have all the files attach in the same email. I am running like this as in my home directory, I have two files: abc.csv and tree.txt.
EmailTest.getInstance().sendEmail("hello#host.com", "hello#host.com", "hello#host.com",
"Test Subject (" + dateFormat.format(new Date()) + ")", content, "/export/home/david/",
Arrays.asList("abc.csv", "tree.txt"));
After I get an email, I don't see my text in the body of an email at all? And second thing is file attachment name is coming as /export/home/david/abc.csv and /export/home/david/tree.txt?
Is anything wrong I am doing? One thing I see wrong as I am calling setContent method twice with different parameters?
First you need to add the text as an own BodyPart. Next your MimeMultipart needs to be set to the type related so you can have both, HTML-Text and some attachements. Then it should work to have both, attachements and text.
And the filename you pass to messageBodyPart.setFileName(filename) is the filename you see in the attachment name. So just leave out the path and you should just see abc.csv and tree.txt
public void sendEmail(final String to, final String from, final String cc, final String subject, final String body,
final String baseDirectory, final List<String> listOfFileNames) {
for (int i = 1; i <= 3; i++) { // retrying
try {
Session session = Session.getInstance(mailProperties, null);
Multipart multipart = new MimeMultipart("related");
MimeBodyPart bodyPart= new MimeBodyPart();
Message message = new MimeMessage(session);
message.setFrom(new InternetAddress(from));
InternetAddress[] toAddress = InternetAddress.parse(to);
InternetAddress[] ccAddress = InternetAddress.parse(cc);
message.addRecipients(RecipientType.TO, toAddress);
message.addRecipients(RecipientType.CC, ccAddress);
message.setSubject(subject);
bodyPart.setText(body, "UTF-8", "html");
multipart.addBodyPart(bodyPart);
for (String file : listOfFileNames) {
String fileLocation = baseDirectory + "/" + file;
addAttachment(multipart, fileLocation, file);
}
message.setContent(multipart);
Transport.send(message, toAddress);
break;
} catch (Exception ex) {
// log exception
}
}
}
// this is used for attachment
private void addAttachment(final Multipart multipart, final String filepath, final String filename) throws MessagingException {
DataSource source = new FileDataSource(filepath);
BodyPart messageBodyPart = new MimeBodyPart();
messageBodyPart.setDataHandler(new DataHandler(source));
messageBodyPart.setFileName(filename);
multipart.addBodyPart(messageBodyPart);
}