Reputation: 13051
i'm developing a client mail using javax.mail to read mail inside mail box:
Properties properties = System.getProperties();
properties.setProperty("mail.store.protocol", "imap");
try {
Session session = Session.getDefaultInstance(properties, null);
Store store = session.getStore("pop3");//create store instance
store.connect("pop3.domain.it", "mail.it", "*****");
Folder inbox = store.getFolder("inbox");
FlagTerm ft = new FlagTerm(new Flags(Flags.Flag.SEEN), false);
inbox.open(Folder.READ_ONLY);//set access type of Inbox
Message messages[] = inbox.search(ft);
String mail,sub,bodyText="";
Object body;
for(Message message:messages) {
mail = message.getFrom()[0].toString();
sub = message.getSubject();
body = message.getContent();
//bodyText = body.....
}
} catch (Exception e) {
System.out.println(e);
}
I know that the method getContent()
returns an object cause the content could be a String
, a MimeMultiPart
, a SharedByteArrayInputstream
and other ( i think )... Is there a way to get always the text inside body of message? Thanks!!
Upvotes: 70
Views: 127682
Reputation: 8565
This answer extends yurin's answer. The issue he brought up was that the content of a MimeMultipart
may itself be another MimeMultipart
. The getTextFromMimeMultipart()
method below recurses in such cases on the content until the message body has been fully parsed.
private String getTextFromMessage(Message message) throws MessagingException, IOException {
if (message.isMimeType("text/plain")) {
return message.getContent().toString();
}
if (message.isMimeType("multipart/*")) {
MimeMultipart mimeMultipart = (MimeMultipart) message.getContent();
return getTextFromMimeMultipart(mimeMultipart);
}
return "";
}
private String getTextFromMimeMultipart(
MimeMultipart mimeMultipart) throws MessagingException, IOException{
String result = "";
for (int i = 0; i < mimeMultipart.getCount(); i++) {
BodyPart bodyPart = mimeMultipart.getBodyPart(i);
if (bodyPart.isMimeType("text/plain")) {
return result + "\n" + bodyPart.getContent(); // without return, same text appears twice in my tests
}
result += this.parseBodyPart(bodyPart);
}
return result;
}
private String parseBodyPart(BodyPart bodyPart) throws MessagingException, IOException {
if (bodyPart.isMimeType("text/html")) {
return "\n" + org.jsoup.Jsoup
.parse(bodyPart.getContent().toString())
.text();
}
if (bodyPart.getContent() instanceof MimeMultipart){
return getTextFromMimeMultipart((MimeMultipart)bodyPart.getContent());
}
return "";
}
Upvotes: 102
Reputation: 5061
In my case I wanted the HTML to be exist also and I also searched for some already made utlity so I fixed mine using following code
import javax.mail.Message;
import org.apache.commons.io.IOUtils;
import javax.mail.internet.MimeUtility;
.....
String body = IOUtils.toString(
MimeUtility.decode(message.getInputStream(), "quoted-printable"),
"UTF-8"
);
Upvotes: 4
Reputation: 50
Here is my code, I use in my IMAP android application. Its working.
GetTextFromMessage returns plain text or html string
Kotlin
@Throws(IOException::class, MessagingException::class)
private fun getTextFromMessage(message: Message): String {
var result: String = ""
if (message.isMimeType("text/plain")) {
result = message.content.toString()
}
else if (message.isMimeType("multipart/*")) {
val mimeMultipart =
message.content as MimeMultipart
result = getTextFromMimeMultipart(mimeMultipart)
}
else if(message.isMimeType("text/html")){
result = message.content.toString()
}
return result
}
@Throws(IOException::class, MessagingException::class)
private fun getTextFromMimeMultipart(
mimeMultipart: MimeMultipart
): String {
val count = mimeMultipart.count
if (count == 0) throw MessagingException("Multipart with no body parts not supported.")
val multipartRelated = ContentType(mimeMultipart.contentType).match("multipart/related")
if(multipartRelated){
val part = mimeMultipart.getBodyPart(0)
val multipartAlt = ContentType(part.contentType).match("multipart/alternative")
if(multipartAlt) {
return getTextFromMimeMultipart(part.content as MimeMultipart)
}
}else{
val multipartAlt = ContentType(mimeMultipart.contentType).match("multipart/alternative")
if (multipartAlt) {
for (i in 0 until count) {
val part = mimeMultipart.getBodyPart(i)
if (part.isMimeType("text/html")) {
return getTextFromBodyPart(part)
}
}
}
}
var result: String = ""
for (i in 0 until count) {
val bodyPart = mimeMultipart.getBodyPart(i)
result += getTextFromBodyPart(bodyPart)
}
return result
}
@Throws(IOException::class, MessagingException::class)
private fun getTextFromBodyPart(
bodyPart: BodyPart
): String {
var result: String = ""
if (bodyPart.isMimeType("text/plain")) {
result = bodyPart.content as String
} else if (bodyPart.isMimeType("text/html")) {
val html = bodyPart.content as String
result = html
} else if (bodyPart.content is MimeMultipart) {
result =
getTextFromMimeMultipart(bodyPart.content as MimeMultipart)
}
return result
}
Upvotes: 0
Reputation: 487
You could use org.apache.commons.mail.util.MimeMessageParser
Java:
String htmlContent = new MimeMessageParser(message).parse().getHtmlContent();
Kotlin:
val htmlContent: String = MimeMessageParser(message).parse().htmlContent
Upvotes: 0
Reputation: 584
My answer is extendeded version of Austin Answer but with one condition in the first method( getTextFromMessage() ).
Change: we should also check whether the MimeType is "text/html".
check lines ending with '//'**
private String getTextFromMessage(Message message) throws MessagingException, IOException {
String result = "";
if (message.isMimeType("text/plain")) {
result = message.getContent().toString();
}
else if (message.isMimeType("text/html")) { // **
result = message.getContent().toString(); // **
}
else if (message.isMimeType("multipart/*")) {
MimeMultipart mimeMultipart = (MimeMultipart) message.getContent();
result = getTextFromMimeMultipart(mimeMultipart);
}
return result;
}
private String getTextFromMimeMultipart(
MimeMultipart mimeMultipart) throws MessagingException, IOException{
String result = "";
int count = mimeMultipart.getCount();
for (int i = 0; i < count; i++) {
BodyPart bodyPart = mimeMultipart.getBodyPart(i);
if (bodyPart.isMimeType("text/plain")) {
result = result + "\n" + bodyPart.getContent();
break; // without break same text appears twice in my tests
} else if (bodyPart.isMimeType("text/html")) {
String html = (String) bodyPart.getContent();
result = result + "\n" + org.jsoup.Jsoup.parse(html).text();
} else if (bodyPart.getContent() instanceof MimeMultipart){
result = result + getTextFromMimeMultipart((MimeMultipart)bodyPart.getContent());
}
}
return result;
}
Upvotes: 0
Reputation: 1320
Don't reinvent the wheel! You can simply use Apache Commons Email (see here)
Kotlin example:
fun readHtmlContent(message: MimeMessage) =
MimeMessageParser(message).parse().htmlContent
If email does not have html content, but it has plain content (you can check that by hasPlainContent and hasHtmlContent methods) then you should use this code:
fun readPlainContent(message: MimeMessage) =
MimeMessageParser(message).parse().plainContent
Java example:
String readHtmlContent(MimeMessage message) throws Exception {
return new MimeMessageParser(message).parse().getHtmlContent();
}
String readPlainContent(MimeMessage message) throws Exception {
return new MimeMessageParser(message).parse().getPlainContent();
}
Upvotes: 27
Reputation: 3085
This answer extends Austin's answer to correct the orginal issue with treatment of multipart/alternative
(// without break same text appears twice in my tests
).
The text appears twice because for multipart/alternative
, the user agent is expected to choose only one part.
From RFC2046:
The "multipart/alternative" type is syntactically identical to "multipart/mixed", but the semantics are different. In particular, each of the body parts is an "alternative" version of the same information.
Systems should recognize that the content of the various parts are interchangeable. Systems should choose the "best" type based on the local environment and references, in some cases even through user interaction. As with "multipart/mixed", the order of body parts is significant. In this case, the alternatives appear in an order of increasing faithfulness to the original content. In general, the best choice is the LAST part of a type supported by the recipient system's local environment.
Same example with treatment for alternatives:
private String getTextFromMessage(Message message) throws IOException, MessagingException {
String result = "";
if (message.isMimeType("text/plain")) {
result = message.getContent().toString();
} else if (message.isMimeType("multipart/*")) {
MimeMultipart mimeMultipart = (MimeMultipart) message.getContent();
result = getTextFromMimeMultipart(mimeMultipart);
}
return result;
}
private String getTextFromMimeMultipart(
MimeMultipart mimeMultipart) throws IOException, MessagingException {
int count = mimeMultipart.getCount();
if (count == 0)
throw new MessagingException("Multipart with no body parts not supported.");
boolean multipartAlt = new ContentType(mimeMultipart.getContentType()).match("multipart/alternative");
if (multipartAlt)
// alternatives appear in an order of increasing
// faithfulness to the original content. Customize as req'd.
return getTextFromBodyPart(mimeMultipart.getBodyPart(count - 1));
String result = "";
for (int i = 0; i < count; i++) {
BodyPart bodyPart = mimeMultipart.getBodyPart(i);
result += getTextFromBodyPart(bodyPart);
}
return result;
}
private String getTextFromBodyPart(
BodyPart bodyPart) throws IOException, MessagingException {
String result = "";
if (bodyPart.isMimeType("text/plain")) {
result = (String) bodyPart.getContent();
} else if (bodyPart.isMimeType("text/html")) {
String html = (String) bodyPart.getContent();
result = org.jsoup.Jsoup.parse(html).text();
} else if (bodyPart.getContent() instanceof MimeMultipart){
result = getTextFromMimeMultipart((MimeMultipart)bodyPart.getContent());
}
return result;
}
Note that this is a very simple example. It misses many cases and should not be used in production in it's current format.
Upvotes: 27
Reputation: 6067
Below is method that will takes text from message in case bodyParts are text and html.
import javax.mail.BodyPart;
import javax.mail.Message;
import javax.mail.internet.MimeMultipart;
import org.jsoup.Jsoup;
....
private String getTextFromMessage(Message message) throws Exception {
if (message.isMimeType("text/plain")){
return message.getContent().toString();
}else if (message.isMimeType("multipart/*")) {
String result = "";
MimeMultipart mimeMultipart = (MimeMultipart)message.getContent();
int count = mimeMultipart.getCount();
for (int i = 0; i < count; i ++){
BodyPart bodyPart = mimeMultipart.getBodyPart(i);
if (bodyPart.isMimeType("text/plain")){
result = result + "\n" + bodyPart.getContent();
break; //without break same text appears twice in my tests
} else if (bodyPart.isMimeType("text/html")){
String html = (String) bodyPart.getContent();
result = result + "\n" + Jsoup.parse(html).text();
}
}
return result;
}
return "";
}
Update. There is a case, that bodyPart itself can be of type multipart. (I met such email after have written this answer.) In this case you will need rewrite above method with recursion.
Upvotes: 15
Reputation: 2794
If you want to get text always then you can skip other types like 'multipart' etc...
Object body = message.getContent();
if(body instanceof String){
// hey it's a text
}
Upvotes: 3
Reputation: 20875
I don't think so, otherwise what would happen if a Part
's mime type is image/jpeg
? The API returns an Object
because internally it tries to give you something useful, provided you know what is expected to be. For general purpose software, it's intended to be used like this:
if (part.isMimeType("text/plain")) {
...
} else if (part.isMimeType("multipart/*")) {
...
} else if (part.isMimeType("message/rfc822")) {
...
} else {
...
}
You also have the raw (actually not so raw, see the Javadoc) Part.getInputStream()
, but I think it's unsafe to assume that each and every message you receive is a text-based one - unless you are writing a very specific application and you have control over the input source.
Upvotes: 10