web-dev-qa-db-ja.com

javax.mailを使用してメール本文内のテキストを読み取る方法

javax.mailを使用してメールボックス内のメールを読み取るクライアントメールを開発しています。

_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);    
}
_

メソッドgetContent()がオブジェクトを返すのは、コンテンツがStringMimeMultiPartSharedByteArrayInputstreamなどである可能性があることを知っています(考えます)。 ..メッセージ本文内のテキストを常に取得する方法はありますか?ありがとう!!

38
JackTurky

この答えは yurinの答え を拡張します。彼が提起した問題は、MimeMultipartの内容自体が別のMimeMultipartである可能性があるということでした。このような場合、以下のgetTextFromMimeMultipart()メソッドは、メッセージ本文が完全に解析されるまでコンテンツに対して再帰します。

private String getTextFromMessage(Message message) throws MessagingException, IOException {
    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 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;
}
58
Austin D

この答えは オースティンの答え を拡張して、multipart/alternative// without break same text appears twice in my tests)。

multipart/alternative、ユーザーエージェントはone部分のみを選択することが期待されます。

RFC2046 から:

「multipart/alternative」タイプは「multipart/mixed」と構文的に同じですが、セマンティクスは異なります。特に、各身体部分は同じ情報の「代替」バージョンです。

システムは、さまざまな部分のコンテンツが交換可能であることを認識する必要があります。システムは、ローカル環境と参照に基づいて、場合によってはユーザーの操作によっても「最適な」タイプを選択する必要があります。 「multipart/mixed」と同様に、身体部分の順序は重要です。この場合、代替は元のコンテンツへの忠実度が高い順に表示されます。一般的に、最良の選択は、受信側システムのローカル環境でサポートされているタイプの最後の部分です。

代替案の扱いと同じ例:

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;
}

これは非常に単純な例であることに注意してください。多くの場合を見逃しており、現在の形式で本番環境で使用すべきではありません。

19
hendalst

以下は、bodyPartsがtextおよび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 "";
}

更新。 bodyPart自体がmultipart型である場合があります。 (この答えを書いた後でこのようなメールに会いました。)この場合、上記のメソッドを再帰的に書き換える必要があります。

10
yurin

そうは思わない、さもなければPartのMIMEタイプが_image/jpeg_の場合はどうなるのだろうか? APIはObjectを返します。これは、何が期待されるかを知っていれば、内部的に有用なものを提供しようとするためです。汎用ソフトウェアの場合、次のように使用することを目的としています。

_if (part.isMimeType("text/plain")) {
   ...
} else if (part.isMimeType("multipart/*")) {
   ...
} else if (part.isMimeType("message/rfc822")) {
   ...
} else {
   ...
}
_

また、raw(実際にはそうではないraw、Javadocを参照) Part.getInputStream() が、受信するすべてのメッセージがテキストベースのものであると想定するのは安全ではないと思います-非常に特定のアプリケーションを作成していて、入力ソースを制御できる場合を除きます。

9
Raffaele

テキストを常に取得したい場合は、「multipart」などの他のタイプをスキップできます。

  Object body = message.getContent(); 
    if(body instanceof String){
    // hey it's a text
    }
4
JAVAGeek

車輪を再発明しないでください!単にApache Commons Emailを使用できます( here を参照)

Kotlinの例:

fun readHtmlContent(message: MimeMessage) = 
        MimeMessageParser(message).parse().htmlContent

メールにhtmlコンテンツはないが、プレーンコンテンツがある場合( hasPlainContent および hasHtmlContent メソッドで確認できます)、このコードを使用する必要があります。

fun readPlainContent(message: MimeMessage) = 
        MimeMessageParser(message).parse().plainContent

Javaの例:

String readHtmlContent(MimeMessage message) throws Exception {
    return new MimeMessageParser(message).parse().getHtmlContent();
}

String readPlainContent(MimeMessage message) throws Exception {
    return new MimeMessageParser(message).parse().getPlainContent();
}
1
grolegor