I am trygin to fetch the html source of an email message so i can parse it.
For some reason content.getCount() returns 2
and sometime the content.getBodyPart(i) is actually a nested Multipart object
How do I distinguish between the elements returned by content.getBodyPart(i)
Is this the correct way to fetch the HTML Source?
ParsedEmailData procesMultiPart(Multipart content){
for (int i = 0; i < content.getCount(); i++) {
BodyPart bodyPart = content.getBodyPart(i);
Object body = bodyPart.getContent();
if(body instanceof Multipart) {
return procesMultiPart((Multipart) body);
}
if (body instanceof String) {
return parsedEmailData = parseEmailBody((String) body);
}
}
In our system we have mailmessages saved and a function where we can forward these messages.
I know want to be able to add text to the top of the message when we forward it.
This has proven to be surprisingly difficult.
Some of the code.
private void addMessageStartOfMail(MimeMessage mail, String forwardMailBody) throws Exception{
Object content = mail.getContent();
if (content.getClass().isAssignableFrom(MimeMultipart.class)) {
MimeMultipart mimeMultipart = (MimeMultipart) content;
for (int i = 0; i < mimeMultipart.getCount(); i++) {
BodyPart bodyPart = mimeMultipart.getBodyPart(i);
if (bodyPart.getContentType().startsWith("text/plain")) {
String cnt = forwardMailBody;
cnt = MailUtil.toPlainText(cnt);
cnt = cnt + (String)bodyPart.getContent();
bodyPart.setContent(cnt, bodyPart.getContentType());
}
......
This works but unfortunatelly not all mails are text/plain, some are text/html and worse, some are multipart which is a total mess.
The trouble code
}else if(bodyPart.getContentType().startsWith("multipart")) {
Multipart mp = (Multipart) bodyPart.getContent();
int count = mp.getCount();
for (int j = 0; j < count; j++) {
BodyPart bp = mp.getBodyPart(j);
if (bp.getContentType().startsWith("text/html")) {
String cnt = form.getForwardMailBody();
cnt = cnt + (String)bp.getContent();
bp.setContent(cnt, bp.getContentType());
....
For some reason this turns the contenttype from html to plain which makes the original message a mess.
I feel there must be a smarter way to this.
Can I somehow add a simple bodypart to beginning of a Mimemessage or something.
Any advice?
After VGRs answer I made a new attempt.
private void addMessageStartOfMail(MimeMessage mail, String forwardMailBody) throws Exception{
Object content = mail.getContent();
if (content.getClass().isAssignableFrom(MimeMultipart.class)) {
MimeMultipart mimeMultipart = (MimeMultipart) content;
BodyPart messageBodyPart = new MimeBodyPart();
messageBodyPart.setText("Test");
mimeMultipart.addBodyPart(messageBodyPart, 0);
}
}
Obviously much cleaner but how do I add the new bodypart. I want it at top of the mail but without overwriting the original mail, which this solution did.
The JavaMail FAQ describes two ways to forward a message.
The simplest way is to create a new message and attach the original message as an attachment.
If instead you want to forward the original message "inline", you need to construct a new body for the message based on the body from the original message. The JavaMail FAQ contains code for finding the main message body. Preserving the original structure of the message with the attachments and so on is much more difficult, especially when you consider all the possible cases of multipart/related, multipart/alternative, etc.
The technique described in the comments above of inserting a new MimeBodyPart into the original message will work in some cases, but it will still be a challenge to find out where exactly to insert it, and the end result may not display as you would like.
I have a requirement where I need to process the first line in the email message and, possibly, forward it.
But the problem happens when this message has attachments. And I need to forward them as well. I just can't find a good example of processing email messages with java.mail in a safe way that would cater for multiple message structures. Also, the forwarding example is a problem.
Can anyone point me to a good resource with some code examples?
Thank you
The code of getting the first line of the email message, forwarding I don't have working:
private String getMessgaeFirstLine(Message msg) throws IOException, MessagingException{
String result = null;
Object objRef = msg.getContent();
Multipart mp = (Multipart) objRef;
int count = mp.getCount();
for (int i = 0; i < count; i++)
{
BodyPart bp = mp.getBodyPart( i );
if (bp instanceof MimeBodyPart )
{
MimeBodyPart mbp = (MimeBodyPart) bp;
if ( mbp.isMimeType( "text/plain" )) {
result = (String) mbp.getContent();
result = result.replaceAll("(\\r|\\n)", "");
break;
}
}
}
return result;
}
The simplest way will be to forward the original message as an attachment to the new message. See the JavaMail FAQ.
Is it possible to get only the count of attachments for a mail in Java? I tried using this:
DataHandler handler = message.getDataHandler();
AttachedFileName= handler.getName();
This lists out all the attachments for all mails inbox but not for specific mails.
Is this possible if so how?
Thanks!
This should give you the attachment count,
Multipart multipart = (Multipart) message.getContent();
int attachmentCount = multipart.getCount();
I don't have enough reputation to comment on the accepted solution:
Multipart multipart = (Multipart) message.getContent();
int attachmentCount = multipart.getCount();
But I don't think that it is ideal for the following reasons:
Many email clients [For example: Thunderbird] send all HTML emails as multipart/alternative. They include a HTML part and an alternative plain text part. Historically, it was done to let clients choose the best alternative that they are capable of displaying.
Not everything included as a part is an attachment. For example, many images are not displayed as attachments in email clients because their disposition is set to "inline".
In summary, this solution potentially counts all HTML emails as having attachments and all emails with inline images as having attachments.
Here is an alternative that ignores parts not normally considered attachments:
private int getAttachmentCount(Message message) {
int count = 0;
try {
Object object = mMessage.getContent();
if (object instanceof Multipart) {
Multipart parts = (Multipart) object;
for (int i = 0; i < parts.getCount(); ++i) {
MimeBodyPart part = (MimeBodyPart) parts.getBodyPart(i);
if (Part.ATTACHMENT.equalsIgnoreCase(part.getDisposition()))
++count;
}
}
} catch (IOException | MessagingException e) {
e.printStackTrace();
}
return count;
}
I know that this solution gets the body part, but I believe that it is the only accurate way to see if it is an attachment.
I had a similar problem. The accepted answer didn't work for me because multipart may not necessarily be an attachment file. I counted the number of attachments by ignoring the other possible cases in multipart.
int count = 0;
Multipart multipart = (Multipart) message.getContent();
for(int i = multipart.getCount() - 1; i >= 0; i--)
{
BodyPart bodyPart = multipart.getBodyPart(i);
String bodyPartContentType = bodyPart.getContentType();
if(isSimpleType(bodyPartContentType)) continue;
else if(isMultipartType(bodyPartContentType)) continue;
else if(!isTextPlain(bodyPartContentType)) count++;
}
You can check simple type, multipart type, and text using these methods:
private boolean isTextPlain(String contentType)
{
return (contentType.contains("TEXT/PLAIN") || (contentType.contains("TEXT/plain")));
}
private boolean isSimpleType(String contentType)
{
if(contentType.contains("TEXT/HTML") || contentType.contains("text") ||
contentType.contains("TEXT/html")) return true;
return false;
}
private boolean isMultipartType(String contentType)
{
if(contentType.contains("multipart") || contentType.contains("multipart/mixed")) return true;
return false;
}
This worked for me.
I am writing a Java application to download emails using Exchange Web Services. I am using Microsoft's ewsjava API for doing this.
I am able to fetch email headers. But, I am not able to download email attachments using this API. Below is the code snippet.
FolderId folderId = new FolderId(WellKnownFolderName.Inbox, "mailbox#example.com");
findResults = service.findItems(folderId, view);
for(Item item : findResults.getItems()) {
if (item.getHasAttachments()) {
AttachmentCollection attachmentsCol = item.getAttachments();
System.out.println(attachmentsCol.getCount()); // This is printing zero all the time. My message has one attachment.
for (int i = 0; i < attachmentsCol.getCount(); i++) {
FileAttachment attachment = (FileAttachment)attachmentsCol.getPropertyAtIndex(i);
String name = attachment.getFileName();
int size = attachment.getContent().length;
}
}
}
item.getHasAttachments() is returning true, but attachmentsCol.getCount() is 0.
You need to load property Attachments before you can use them in your code. You set it for ItemView object that you pass to FindItems method.
Or you can first find items and then call service.LoadPropertiesForItems and pass findIesults and PropertySet object with added EmailMessageSchema.Attachments
FolderId folderId = new FolderId(WellKnownFolderName.Inbox, "mailbox#example.com");
findResults = service.findItems(folderId, view);
service.loadPropertiesForItems(findResults, new PropertySet(BasePropertySet.FirstClassProperties, EmailMessageSchema.Attachments));
for(Item item : findResults.getItems()) {
if (item.getHasAttachments()) {
AttachmentCollection attachmentsCol = item.getAttachments();
System.out.println(attachmentsCol.getCount());
for (int i = 0; i < attachmentsCol.getCount(); i++) {
FileAttachment attachment = (FileAttachment)attachmentsCol.getPropertyAtIndex(i);
attachment.load(attachment.getName());
}
}
}
Honestly as painful as it is, I'd use the PROXY version instead of the Managed API. It's a pity, but the managed version for java seems riddled with bugs.
before checking for item.getHasAttachments(), you should do item.load(). Otherwise there is a chance your code will not load the attachment and attachmentsCol.getCount() will be 0.
Working code with Exchange Server 2010 :
ItemView view = new ItemView(Integer.MAX_VALUE);
view.getOrderBy().add(ItemSchema.DateTimeReceived, SortDirection.Descending);
FindItemsResults < Item > results = service.findItems(WellKnownFolderName.Inbox, new SearchFilter.IsEqualTo(EmailMessageSchema.IsRead, true), view);
Iterator<Item> itr = results.iterator();
while(itr.hasNext()) {
Item item = itr.next();
item.load();
ItemId itemId = item.getId();
EmailMessage email = EmailMessage.bind(service, itemId);
if (item.getHasAttachments()) {
System.err.println(item.getAttachments());
AttachmentCollection attachmentsCol = item.getAttachments();
for (int i = 0; i < attachmentsCol.getCount(); i++) {
FileAttachment attachment=(FileAttachment)attachmentsCol.getPropertyAtIndex(i);
attachment.load("C:\\TEMP\\" +attachment.getName());
}
}
}
Little late for the answer, but here is what I have.
HashMap<String, HashMap<String, String>> attachments = new HashMap<String, HashMap<String, String>>();
if (emailMessage.getHasAttachments() || emailMessage.getAttachments().getItems().size() > 0) {
//get all the attachments
AttachmentCollection attachmentsCol = emailMessage.getAttachments();
log.info("File Count: " +attachmentsCol.getCount());
//loop over the attachments
for (int i = 0; i < attachmentsCol.getCount(); i++) {
Attachment attachment = attachmentsCol.getPropertyAtIndex(i);
//log.debug("Starting to process attachment "+ attachment.getName());
//FileAttachment - Represents a file that is attached to an email item
if (attachment instanceof FileAttachment || attachment.getIsInline()) {
attachments.putAll(extractFileAttachments(attachment, properties));
} else if (attachment instanceof ItemAttachment) { //ItemAttachment - Represents an Exchange item that is attached to another Exchange item.
attachments.putAll(extractItemAttachments(service, attachment, properties, appendedBody));
}
}
}
} else {
log.debug("Email message does not have any attachments.");
}
//Extract File Attachments
try {
FileAttachment fileAttachment = (FileAttachment) attachment;
// if we don't call this, the Content property may be null.
fileAttachment.load();
//extract the attachment content, it's not base64 encoded.
attachmentContent = fileAttachment.getContent();
if (attachmentContent != null && attachmentContent.length > 0) {
//check the size
int attachmentSize = attachmentContent.length;
//check if the attachment is valid
ValidateEmail.validateAttachment(fileAttachment, properties,
emailIdentifier, attachmentSize);
fileAttachments.put(UtilConstants.ATTACHMENT_SIZE, String.valueOf(attachmentSize));
//get attachment name
String fileName = fileAttachment.getName();
fileAttachments.put(UtilConstants.ATTACHMENT_NAME, fileName);
String mimeType = fileAttachment.getContentType();
fileAttachments.put(UtilConstants.ATTACHMENT_MIME_TYPE, mimeType);
log.info("File Name: " + fileName + " File Size: " + attachmentSize);
if (attachmentContent != null && attachmentContent.length > 0) {
//convert the content to base64 encoded string and add to the collection.
String base64Encoded = UtilFunctions.encodeToBase64(attachmentContent);
fileAttachments.put(UtilConstants.ATTACHMENT_CONTENT, base64Encoded);
}
//Extract Item Attachment
try {
ItemAttachment itemAttachment = (ItemAttachment) attachment;
PropertySet propertySet = new PropertySet(
BasePropertySet.FirstClassProperties, ItemSchema.Attachments,
ItemSchema.Body, ItemSchema.Id, ItemSchema.DateTimeReceived,
EmailMessageSchema.DateTimeReceived, EmailMessageSchema.Body);
itemAttachment.load();
propertySet.setRequestedBodyType(BodyType.Text);
Item item = itemAttachment.getItem();
eBody = appendItemBody(item, appendedBody.get(UtilConstants.BODY_CONTENT));
appendedBody.put(UtilConstants.BODY_CONTENT, eBody);
/*
* We need to check if Item attachment has further more
* attachments like .msg attachment, which is an outlook email
* as attachment. Yes, we can attach an email chain as
* attachment and that email chain can have multiple
* attachments.
*/
AttachmentCollection childAttachments = item.getAttachments();
//check if not empty collection. move on
if (childAttachments != null && !childAttachments.getItems().isEmpty() && childAttachments.getCount() > 0) {
for (Attachment childAttachment : childAttachments) {
if (childAttachment instanceof FileAttachment) {
itemAttachments.putAll(extractFileAttachments(childAttachment, properties, emailIdentifier));
} else if (childAttachment instanceof ItemAttachment) {
itemAttachments = extractItemAttachments(service, childAttachment, properties, appendedBody, emailIdentifier);
}
}
}
} catch (Exception e) {
throw new Exception("Exception while extracting Item Attachments: " + e.getMessage());
}