【问题标题】:How to read text inside body of mail using javax.mail如何使用 javax.mail 读取邮件正文中的文本
【发布时间】:2012-06-29 17:18:05
【问题描述】:

我正在使用 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 和其他(我认为)......有没有办法让消息正文中的文本?谢谢!!

【问题讨论】:

  • 你得到什么样的输出???你不能利用msg.getContentType()来识别类型并根据类型处理邮件吗??
  • 我不需要知道内容是什么类型,我只需要知道里面的文字
  • 每个具有不同 MIME 类型的邮件都需要以不同的方式处理才能获得文本。所以你需要切换使用getContentType
  • 这里有一个非常奇怪的 POP3 和 IMAP 组合。

标签: java mime multipart jakarta-mail


【解决方案1】:

在我的情况下,我希望 HTML 也存在,并且我还搜索了一些已经制作的实用程序,所以我使用以下代码修复了我的 HTML

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"
              );

【讨论】:

  • 像魅力一样工作:)
  • 保佑你的灵魂 - 在这里尝试了几个答案,这个很管用。
【解决方案2】:

这是我的代码,我在我的 IMAP android 应用程序中使用。它的工作。

GetTextFromMessage 返回纯文本或 html 字符串

科特林

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

【讨论】:

    【解决方案3】:

    你可以使用org.apache.commons.mail.util.MimeMessageParser

    Java:

    String htmlContent = new MimeMessageParser(message).parse().getHtmlContent();
    

    科特林:

    val htmlContent: String = MimeMessageParser(message).parse().htmlContent
    

    【讨论】:

      【解决方案4】:

      我的答案是Austin Answer 的扩展版本,但在第一种方法中有一个条件( getTextFromMessage() )。

      更改:我们还应该检查 MimeType 是否为“text/html”。

      检查以'//'**结尾的行

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

      【讨论】:

        【解决方案5】:

        不要重新发明轮子!您可以简单地使用 Apache Commons 电子邮件(请参阅 here

        Kotlin 示例:

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

        如果电子邮件没有 html 内容,但它有纯内容(您可以通过 hasPlainContenthasHtmlContent 方法检查),那么您应该使用以下代码:

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

        【讨论】:

        • 这真是太棒了! Java 部分完美地完成了这个技巧,而且简单干净
        • 给这个人一个奖!我试图实现这个库基本上是徒劳的三天。谢啦!你是救生员:)
        【解决方案6】:

        这个答案扩展了yurin's answer。他提出的问题是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;
        }
        

        【讨论】:

        • JFYI:在 Orcale 的 JavaMail 常见问题解答中,您以不同的方式处理 multipart/alternativeoracle.com/technetwork/java/javamail/faq/index.html#mainbody。不知道他们为什么这样做,因为我不熟悉multipart
        • @AbhishekGupta 不同之处在于multipart/alternative,用户代理应该只选择一个部分,而不是连接。 FAQ 代码会这样做,而上面的代码不会。请参阅下面的答案以了解更多详细信息。
        【解决方案7】:

        此答案扩展了 Austin's answer 以更正处理 multipart/alternative (// without break same text appears twice in my tests) 的原始问题。

        文本出现两次,因为对于multipart/alternative,用户代理应该只选择一个部分。

        来自RFC2046

        “multipart/alternative”类型在语法上与“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;
        }
        

        请注意,这是一个非常简单的示例。它遗漏了许多情况,不应以当前格式在生产中使用。

        【讨论】:

        • java.lang.ClassCastException: javax.mail.util.SharedByteArrayInputStream 无法转换为 javax.mail.internet.MimeMultipart 我收到此错误
        • 这是一个非常好的例子——目前互联网上最好的例子,谢谢。
        • 对于 gmail,这不会返回邮件正文。它总是返回空指针 @String html = (String) bodyPart.getContent(); 可能是什么问题?
        • 这个例子完全符合我的设想。 Mail .eml 消息可以具有复杂的层次结构,并且看起来,此类能够包含所有情况。另外我必须说,libray javax.mail 快速可靠。不错的选择。
        • 我不明白他们为什么不提供.getParts() 我们可以迭代然后确定我们想要哪个。我们甚至可以做一个过滤器。相反,我们必须做 0, 1, 2, 3....
        【解决方案8】:

        如果bodyParts是文本和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 类型。 (我在写完这个答案后遇到了这样的电子邮件。)在这种情况下,你需要用递归重写上述方法。

        【讨论】:

        • //without break same text appears twice in my tests - 这是因为您没有区分 multipart/alternativemultipart/mixedmultipart/alternative 表示这些部分包含相同的信息,但表示形式不同。在这种情况下,用户代理应该只选择一个。见here
        • @hendlast 谢谢。
        • 欢迎。有关如何处理此问题的示例,请参见下文。一般来说(根据 RFC),您应该使用最后一个元素,尽管在这种情况下首选纯文本,因此循环遍历正文部分以查找计划文本版本可能是理想的。
        【解决方案9】:

        如果您想始终获取文本,则可以跳过其他类型,例如“多部分”等...

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

        【讨论】:

        • 这样我跳转邮件对吗?
        • instanceOf 只是一个运算符而不是 java 中的方法,这只是返回主题而不是消息正文
        【解决方案10】:

        我不这么认为,否则如果Part 的mime 类型是image/jpeg 会怎样? API 返回一个Object,因为在内部它会尝试为您提供一些有用的东西,前提是您知道预期会是什么。对于通用软件,它打算这样使用:

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

        您也有原始的(实际上并非如此原始,请参阅 Javadoc)Part.getInputStream(),但我认为假设您收到的每条消息都是基于文本的消息是不安全的 -除非您正在编写一个非常具体的应用程序并且您可以控制输入源。

        【讨论】:

        猜你喜欢
        • 2021-01-30
        • 1970-01-01
        • 1970-01-01
        • 2018-04-15
        • 2016-07-04
        • 1970-01-01
        • 2021-01-23
        • 2020-08-04
        • 1970-01-01
        相关资源
        最近更新 更多