Laden Sie Anhänge mit Java Mail herunter

95

Nachdem ich alle Nachrichten heruntergeladen und gespeichert habe

Message[] temp;

Wie erhalte ich die Liste der Anhänge für jede dieser Nachrichten an

List<File> attachments;

Hinweis: Bitte keine Bibliotheken von Drittanbietern, nur JavaMail.

Folone
quelle

Antworten:

109

Ohne Ausnahmebehandlung, aber hier geht's:

List<File> attachments = new ArrayList<File>();
for (Message message : temp) {
    Multipart multipart = (Multipart) message.getContent();

    for (int i = 0; i < multipart.getCount(); i++) {
        BodyPart bodyPart = multipart.getBodyPart(i);
        if(!Part.ATTACHMENT.equalsIgnoreCase(bodyPart.getDisposition()) &&
               StringUtils.isBlank(bodyPart.getFileName())) {
            continue; // dealing with attachments only
        } 
        InputStream is = bodyPart.getInputStream();
        // -- EDIT -- SECURITY ISSUE --
        // do not do this in production code -- a malicious email can easily contain this filename: "../etc/passwd", or any other path: They can overwrite _ANY_ file on the system that this code has write access to!
//      File f = new File("/tmp/" + bodyPart.getFileName());
        FileOutputStream fos = new FileOutputStream(f);
        byte[] buf = new byte[4096];
        int bytesRead;
        while((bytesRead = is.read(buf))!=-1) {
            fos.write(buf, 0, bytesRead);
        }
        fos.close();
        attachments.add(f);
    }
}
David Rabinowitz
quelle
2
Aber warten Sie eine Minute, sollten wir nicht prüfen, ob (bodyPart.getDisposition () == Part.ATTACHMENT) {}, bevor Sie die Datei speichern, damit der Text der E-Mail nicht gespeichert wird?
Folone
8
Wäre StringUtils.isBlank () nicht natürlicher zu lesen als! StringUtils.isNotBlank?
Kuchi
Diese Antwort berücksichtigt keine verschachtelten mehrteiligen Anhänge (die beispielsweise häufig von Thunderbird verwendet werden). Informationen zum Auffinden verschachtelter mehrteiliger Anhänge finden Sie unter @ mefi-Antwort.
Ruslan Stelmachenko
2
Das Snippet ist eine Sicherheitsverletzung, die darauf wartet, passiert zu werden. Ich habe das Snippet bearbeitet, um dies hervorzuheben.
Rzwitserloot
1
Natürlich und danke dafür. Dieses Snippet sollte nur zeigen, wie es gemacht werden kann, natürlich ist es kein Produktionscode
David Rabinowitz
32

Die Frage ist sehr alt, aber vielleicht hilft sie jemandem. Ich möchte die Antwort von David Rabinowitz erweitern.

if(!Part.ATTACHMENT.equalsIgnoreCase(bodyPart.getDisposition()))

sollte nicht alle Anhänge wie erwartet zurückgeben, da Sie E-Mails haben können, bei denen gemischte Teile ohne definierte Disposition sind.

   ----boundary_328630_1e15ac03-e817-4763-af99-d4b23cfdb600
Content-Type: application/octet-stream;
    name="00000000009661222736_236225959_20130731-7.txt"
Content-Transfer-Encoding: base64

In diesem Fall können Sie auch nach Dateinamen suchen. So was:

if (!Part.ATTACHMENT.equalsIgnoreCase(part.getDisposition()) && StringUtils.isBlank(part.getFileName())) {...}

BEARBEITEN

Es gibt einen vollständigen Arbeitscode unter Verwendung der oben beschriebenen Bedingung. Da jedes Teil andere Teile einkapseln kann und der Anhang verschachtelt sein sollte, wird die Rekursion verwendet, um alle Teile zu durchlaufen

public List<InputStream> getAttachments(Message message) throws Exception {
    Object content = message.getContent();
    if (content instanceof String)
        return null;        

    if (content instanceof Multipart) {
        Multipart multipart = (Multipart) content;
        List<InputStream> result = new ArrayList<InputStream>();

        for (int i = 0; i < multipart.getCount(); i++) {
            result.addAll(getAttachments(multipart.getBodyPart(i)));
        }
        return result;

    }
    return null;
}

private List<InputStream> getAttachments(BodyPart part) throws Exception {
    List<InputStream> result = new ArrayList<InputStream>();
    Object content = part.getContent();
    if (content instanceof InputStream || content instanceof String) {
        if (Part.ATTACHMENT.equalsIgnoreCase(part.getDisposition()) || StringUtils.isNotBlank(part.getFileName())) {
            result.add(part.getInputStream());
            return result;
        } else {
            return new ArrayList<InputStream>();
        }
    }

    if (content instanceof Multipart) {
            Multipart multipart = (Multipart) content;
            for (int i = 0; i < multipart.getCount(); i++) {
                BodyPart bodyPart = multipart.getBodyPart(i);
                result.addAll(getAttachments(bodyPart));
            }
    }
    return result;
}
Mefi
quelle
Der Ausdruck prüft, ob der Dateiname leer ist oder null. Ist das korrekt?
Keerthivasan
Octopus: Ja. Überprüft, ob eine CharSequence nicht leer (""), nicht null und nicht nur Leerzeichen ist.
Mefi
Hey, kannst du sagen, wie man die Liste <InputStream> in die Liste <Datei> konvertiert?
Kumuda
kumunda: hi, du solltest diese Liste iterieren und org.apache.commons.io.FileUtils.copyInputStreamToFile (InputStream-Quelle, Dateiziel) verwenden und jede Datei einer anderen Sammlung hinzufügen. Wenn Sie Guava verwenden, überprüfen Sie Lists.transform (...), das Sie instad of iteration verwenden können (abhängig davon, wie Sie jede
Dateiinstanz
9

Zeitersparnis für den Code, in dem Sie die Anhangsdatei speichern:

mit javax mail version 1.4 und danach kann man sagen

// SECURITY LEAK - do not do this! Do not trust the 'getFileName' input. Imagine it is: "../etc/passwd", for example.
// bodyPart.saveFile("/tmp/" + bodyPart.getFileName());

anstatt

    InputStream is = bodyPart.getInputStream();
    File f = new File("/tmp/" + bodyPart.getFileName());
    FileOutputStream fos = new FileOutputStream(f);
    byte[] buf = new byte[4096];
    int bytesRead;
    while((bytesRead = is.read(buf))!=-1) {
        fos.write(buf, 0, bytesRead);
    }
    fos.close();
kommradHomer
quelle
3
Anscheinend bodyPart sollte zuerst konvertiert werden MimeBodyPart, wie zum Beispiel:((MimeBodyPart) bodyPart).saveFile("/tmp/" + bodyPart.getFileName());
yair
5

Sie können einfach die Apache Commons Mail-API MimeMessageParser - getAttachmentList () zusammen mit Commons IO und Commons Lang verwenden.

MimeMessageParser parser = ....
parser.parse();
for(DataSource dataSource : parser.getAttachmentList()) {

    if (StringUtils.isNotBlank(dataSource.getName())) {}

        //use apache commons IOUtils to save attachments
        IOUtils.copy(dataSource.getInputStream(), ..dataSource.getName()...)
    } else {
        //handle how you would want attachments without file names
        //ex. mails within emails have no file name
    }
}
Stackee007
quelle