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.
Related
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 use javax.mail to download mails from a given mail address in order to get the attachments (I expect images) and save the images on disk automatically (polling the mail address). This works fine except if the mail has been sent from an iPhone. It seems that in these cases the image is embedded in the mail (I can see the image in the web mail window) and cannot be downloaded as an attachment.
How can I extract the image from the mail?
What is the difference between iPhone mails and other mails regarding attachments?
Is the image a special part of the mail content?
In my program log I can see:
- contentType: multipart/mixed; boundary=Apple-Mail-...
- numberOfParts = 2
Java version is 1.7.0_21
javax.mail version is 1.4.7
This is the relevant code (most of it taken from http://www.codejava.net)
if (contentType.contains("multipart")) {
// content may contain attachments
Multipart multiPart = (Multipart) message.getContent();
numberOfParts = multiPart.getCount();
for (int partCount = 0; partCount < numberOfParts; partCount++) {
MimeBodyPart part = (MimeBodyPart) multiPart.getBodyPart(partCount);
if (Part.ATTACHMENT.equalsIgnoreCase(part.getDisposition())) {
// this part is the attachment
String fileName = part.getFileName();
attachFiles += fileName + ", ";
if (fileName.endsWith("jpg") || fileName.endsWith("JPG")
|| fileName.endsWith("jpeg") || fileName.endsWith("JPEG")) {
part.saveFile(saveDirectory + File.separator + fileName);
} else {
// attachment is not an image
}
} else {
// this part may be the message content
messageContent = part.getContent().toString();
}
}
if (attachFiles.length() > 1) {
attachFiles = attachFiles.substring(0, attachFiles.length() - 2);
}
} else if (contentType.contains("text/plain") || contentType.contains("text/html")) {
Object content = message.getContent();
if (content != null) {
messageContent = content.toString();
}
}
Below code can be checked:
Multipart mp = new MimeMultipart("related")
Use the default constructor,which resolves the issue.
The code you have is full of assumptions about the structure of a message. Most likely, one of those assumptions is wrong. Fire up a debugger, add some print statements, or do whatever is necessary to step through your code and compare what you're actually getting with what you expect to get. You can also dump the raw MIME content of the message using the Message.writeTo method, to see what the MIME structure of the message really is.
Probably the first thing to check is whether the image is marked as an ATTACHMENT. Perhaps it's being sent as INLINE instead?
BTW, you never want to use the filename in the message directly; someone could send you all sorts of malicious junk in there.
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.
I wrote an application which gets all emails from an inbox, filters the emails which contain a specific string and then puts those emails in an ArrayList.
After the emails are put in the List, I am doing some stuff with the subject and content of said emails. This works all fine for e-mails without an attachment. But when I started to use e-mails with attachments it all didn't work as expected anymore.
This is my code:
public void getInhoud(Message msg) throws IOException {
try {
cont = msg.getContent();
} catch (MessagingException ex) {
Logger.getLogger(ReadMailNew.class.getName()).log(Level.SEVERE, null, ex);
}
if (cont instanceof String) {
String body = (String) cont;
} else if (cont instanceof Multipart) {
try {
Multipart mp = (Multipart) msg.getContent();
int mp_count = mp.getCount();
for (int b = 0; b < 1; b++) {
dumpPart(mp.getBodyPart(b));
}
} catch (Exception ex) {
System.out.println("Exception arise at get Content");
ex.printStackTrace();
}
}
}
public void dumpPart(Part p) throws Exception {
email = null;
String contentType = p.getContentType();
System.out.println("dumpPart" + contentType);
InputStream is = p.getInputStream();
if (!(is instanceof BufferedInputStream)) {
is = new BufferedInputStream(is);
}
int c;
final StringWriter sw = new StringWriter();
while ((c = is.read()) != -1) {
sw.write(c);
}
if (!sw.toString().contains("<div>")) {
mpMessage = sw.toString();
getReferentie(mpMessage);
}
}
The content from the e-mail is stored in a String.
This code works all fine when I try to read mails without attachment. But if I use an e-mail with attachment the String also contains HTML code and even the attachment coding. Eventually I want to store the attachment and the content of an e-mail, but my first priority is to get just the text without any HTML or attachment coding.
Now I tried an different approach to handle the different parts:
public void getInhoud(Message msg) throws IOException {
try {
Object contt = msg.getContent();
if (contt instanceof Multipart) {
System.out.println("Met attachment");
handleMultipart((Multipart) contt);
} else {
handlePart(msg);
System.out.println("Zonder attachment");
}
} catch (MessagingException ex) {
ex.printStackTrace();
}
}
public static void handleMultipart(Multipart multipart)
throws MessagingException, IOException {
for (int i = 0, n = multipart.getCount(); i < n; i++) {
handlePart(multipart.getBodyPart(i));
System.out.println("Count "+n);
}
}
public static void handlePart(Part part)
throws MessagingException, IOException {
String disposition = part.getDisposition();
String contentType = part.getContentType();
if (disposition == null) { // When just body
System.out.println("Null: " + contentType);
// Check if plain
if ((contentType.length() >= 10)
&& (contentType.toLowerCase().substring(
0, 10).equals("text/plain"))) {
part.writeTo(System.out);
} else if ((contentType.length() >= 9)
&& (contentType.toLowerCase().substring(
0, 9).equals("text/html"))) {
part.writeTo(System.out);
} else if ((contentType.length() >= 9)
&& (contentType.toLowerCase().substring(
0, 9).equals("text/html"))) {
System.out.println("Ook html gevonden");
part.writeTo(System.out);
}else{
System.out.println("Other body: " + contentType);
part.writeTo(System.out);
}
} else if (disposition.equalsIgnoreCase(Part.ATTACHMENT)) {
System.out.println("Attachment: " + part.getFileName()
+ " : " + contentType);
} else if (disposition.equalsIgnoreCase(Part.INLINE)) {
System.out.println("Inline: "
+ part.getFileName()
+ " : " + contentType);
} else {
System.out.println("Other: " + disposition);
}
}
This is what is returned from the System.out.printlns
Null: multipart/alternative; boundary=047d7b6220720b499504ce3786d7
Other body: multipart/alternative; boundary=047d7b6220720b499504ce3786d7
Content-Type: multipart/alternative; boundary="047d7b6220720b499504ce3786d7"
--047d7b6220720b499504ce3786d7
Content-Type: text/plain; charset="ISO-8859-1"
'Text of the message here in normal text'
--047d7b6220720b499504ce3786d7
Content-Type: text/html; charset="ISO-8859-1"
Content-Transfer-Encoding: quoted-printable
'HTML code of the message'
This approach returns the normal text of the e-mail but also the HTML coding of the mail. I really don't understand why this happens, I've googled it but it seems like there is no one else with this problem.
Any help is appreciated,
Thanks!
I found reading e-mail with the JavaMail library much more difficult than expected. I don't blame the JavaMail API, rather I blame my poor understanding of RFC-5322 -- the official definition of Internet e-mail.
As a thought experiment: Consider how complicated an e-mail message can become in the real world. It is possible to "infinitely" embed messages within messages. Each message itself may have multiple attachments (binary or human-readable text). Now imagine how complicated this structure becomes in the JavaMail API after parsing.
A few tips that may help when traversing e-mail with JavaMail:
Message and BodyPart both implement Part.
MimeMessage and MimeBodyPart both implement MimePart.
Where possible, treat everything as a Part or MimePart. This will allow generic traversal methods to be built more easily.
These Part methods will help to traverse:
String getContentType(): Starts with the MIME type. You may be tempted to treat this as a MIME type (with some hacking/cutting/matching), but don't. Better to only use this method inside the debugger for inspection.
Oddly, MIME type cannot be extracted directly. Instead use boolean isMimeType(String) to match. Read docs carefully to learn about powerful wildcards, such as "multipart/*".
Object getContent(): Might be instanceof:
Multipart -- container for more Parts
Cast to Multipart, then iterate as zero-based index with int getCount() and BodyPart getBodyPart(int)
Note: BodyPart implements Part
In my experience, Microsoft Exchange servers regularly provide two copies of the body text: plain text and HTML.
To match plain text, try: Part.isMimeType("text/plain")
To match HTML, try: Part.isMimeType("text/html")
Message (implements Part) -- embedded or attached e-mail
String (just the body text -- plain text or HTML)
See note above about Microsoft Exchange servers.
InputStream (probably a BASE64-encoded attachment)
String getDisposition(): Value may be null
if Part.ATTACHMENT.equalsIgnoreCase(getDisposition()), then call getInputStream() to get raw bytes of the attachment.
Finally, I found the official Javadocs exclude everything in the com.sun.mail package (and possibly more). If you need these, read the code directly, or generate the unfiltered Javadocs by downloading the source and running mvn javadoc:javadoc in the mail project module of the project.
Did you find these JavaMail FAQ entries?
How do I read a message with an attachment and save the attachment?
How do I tell if a message has attachments?
How do I find the main message body in a message that has attachments?
Following up on Kevin's helpful advice, analyzing your email content Java object types with respect to their canonical names (or simple names) can be helpful too. For example, looking at one inbox I've got right now, of 486 messages 399 are Strings, and 87 are MimeMultipart. This suggests that - for my typical email - a strategy that uses instanceof to first peel off Strings is best.
Of the Strings, 394 are text/plain, and 5 are text/html. This will not be the case for most; it's reflective of my email feeds into this particular inbox.
But wait - there's more!!! :-) The HTML sneaks in there nevertheless: of the 87 Multipart's, 70 are multipart/alternative. No guarantees, but most (if not all of these) are TEXT + HTML.
Of the other 17 multipart, incidentally, 15 are multipart/mixed, and 2 are multipart/signed.
My use case with this inbox (and one other) is primarily to aggregate and analyze known mailing list content. I can't ignore any of the messages, but an analysis of this sort helps me make my processing more efficient.