Jak obsługiwać pocztę wieloczęściową/alternatywną za pomocą JavaMail?

Napisałem aplikację, która pobiera wszystkie e-maile ze skrzynki odbiorczej, filtruje e-maile, które zawierają określony ciąg znaków, a następnie umieszcza te e-maile w ArrayList.

Po umieszczeniu emaili na liście, robię kilka rzeczy z tematem i treścią wspomnianych e-maili. To działa dobrze dla e-maili bez załącznika. Ale kiedy zacząłem używać e-maili z załącznikami, to wszystko nie działało zgodnie z oczekiwaniami.

To jest mój kod:

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

Treść z e-mail jest przechowywany w ciągu znaków.

Ten kod działa dobrze, gdy próbuję czytać maile bez załącznika. Ale jeśli używam e-maila z załącznikiem, ciąg zawiera również kod HTML, a nawet kodowanie załącznika. W końcu chcę zapisać załącznik I treść wiadomości e-mail, ale moim priorytetem jest uzyskanie tylko tekstu bez kodowania HTML lub załączników.

Teraz próbowałem innego podejścia do obsługi różnych części:

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

To jest to, co jest zwracane z 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'

To podejście zwraca normalny tekst wiadomości e-mail, ale także kodowanie HTML wiadomości. Naprawdę nie rozumiem, dlaczego tak się dzieje, wygooglowałem to, ale wydaje się, że nie ma nikogo innego z tym problemem.

Każda pomoc jest doceniana,

Dzięki!
Author: Jef, 2012-11-11

3 answers

Czytanie e-maili za pomocą biblioteki JavaMail było o wiele trudniejsze niż się spodziewano. Nie winię API JavaMail, raczej winię moje słabe zrozumienie RFC-822 -- oficjalnej definicji internetowej poczty elektronicznej.

Jako eksperyment myślowy: zastanów się, jak skomplikowana może stać się wiadomość e-mail w prawdziwym świecie. Możliwe jest" nieskończenie " osadzanie wiadomości w wiadomościach. Każda wiadomość może zawierać wiele załączników (tekst binarny lub tekst czytelny dla człowieka). Teraz wyobraź sobie, jak skomplikowana staje się ta struktura w JavaMail API po przetworzeniu.

Kilka wskazówek, które mogą pomóc podczas przechodzenia poczty e-mail za pomocą JavaMail:]}

Message, Multipart, i BodyPart wszystkie implementacje Part. Jeśli to możliwe, traktuj wszystko jako Part. Pozwoli to na łatwiejsze zbudowanie ogólnych metod trawersowania.

Te Part metody pomogą przejść:

  • String getContentType(): zaczyna się od typu MIME. Można się pokusić o traktowanie tego jako typu MIME (z niektóre hacking/cutting/matching), ale nie. lepiej używać tylko tej metody wewnątrz debuggera do kontroli.
    • co dziwne, typ MIME nie może być wyodrębniony bezpośrednio. Zamiast tego użyj boolean isMimeType(String), aby dopasować. Przeczytaj uważnie dokumenty, aby dowiedzieć się więcej o potężnych kartach wieloznacznych, takich jak "multipart/*".
  • Object getContent(): może być instanceof:
    • Multipart -- pojemnik na więcej Parts
      • wrzuć do Multipart, a następnie iteratuj jako indeks bazujący na zero z int getCount() i BodyPart getBodyPart(int)
        • Uwaga: BodyPart implementuje Part
      • Z mojego doświadczenia wynika, że serwery Microsoft Exchange regularnie dostarczają dwie kopie tekstu źródłowego: zwykły tekst i HTML.
        • aby dopasować zwykły tekst, spróbuj: Part.isMimeType("text/plain")
        • aby dopasować HTML, spróbuj: Part.isMimeType("text/html")
    • Message (implementuje Part) -- embedded or attached e-mail
    • String (tylko tekst główny -- zwykły tekst lub HTML)
      • Zobacz notatkę powyżej o serwerach Microsoft Exchange.
    • InputStream (prawdopodobnie załącznik kodowany BASE64)
  • String getDisposition(): wartość może być null
    • if Part.ATTACHMENT.equalsIgnoreCase(getDisposition()), następnie wywołaj getInputStream(), Aby uzyskać surowe bajty załącznika.

W końcu znalazłem oficjalne Javadocs wykluczają wszystko w com.sun.mail pakiecie(i ewentualnie więcej). Jeśli potrzebujesz, przeczytaj kod bezpośrednio lub Wygeneruj niefiltrowane Javadocs przez pobierając źródło i uruchamiając mvn javadoc:javadoc w module mail projektu projekt.

 20
Author: kevinarpe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-19 15:09:26
 7
Author: Bill Shannon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-11-11 19:17:24

Podążając za pomocnymi poradami Kevina, pomocne może być również analizowanie treści wiadomości e-mail typów obiektów Java w odniesieniu do ich kanonicznych nazw (lub nazw prostych). Na przykład, patrząc na jedną skrzynkę odbiorczą, którą mam teraz, z 486 wiadomości 399 to ciągi, A 87 to Mimemultipart. To sugeruje , że dla mojego typowego e - maila najlepsza jest strategia, która używa instanceof do pierwszego odrywania łańcuchów.

Z łańcuchów 394 to text / plain, a 5 to text / html. To nie będzie sprawa dla większości; to odzwierciedlenie mojego adresu e-mail do tej konkretnej skrzynki odbiorczej.

Ale czekaj - to nie wszystko!!! :- ) HTML zakrada się tam jednak: z 87 Multipart ' ów, 70 to multipart / alternatywa. Brak gwarancji, ale większość (jeśli nie wszystkie) to tekst + HTML.

Z pozostałych 17 wieloczęściowych, nawiasem mówiąc, 15 to wieloczęściowe / mieszane, a 2 to wieloczęściowe / podpisane.

Moim przypadkiem użycia tej skrzynki odbiorczej (i jednej innej) jest przede wszystkim agregacja i analiza znanych list dyskusyjnych treść. Nie mogę zignorować żadnej wiadomości, ale analiza tego rodzaju pomaga mi uczynić moje przetwarzanie bardziej wydajnym.

 1
Author: Arved Sandstrom,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-17 15:29:02