Télécharger les pièces jointes à l'aide de Java Mail

96

Maintenant que j'ai téléchargé tous les messages et les stocke dans

Message[] temp;

Comment puis-je obtenir la liste des pièces jointes pour chacun de ces messages à

List<File> attachments;

Remarque: pas de bibliothèques tierces, s'il vous plaît, juste JavaMail.

folone
la source

Réponses:

109

Sans traitement d'exception, mais voici:

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
la source
2
Mais attendez une minute, ne sommes-nous pas censés vérifier si (bodyPart.getDisposition () == Part.ATTACHMENT) {} avant de sauvegarder le fichier, afin qu'il ne sauvegarde pas le corps de l'e-mail?
folone
8
Est-ce que StringUtils.isBlank () ne serait pas plus naturel à lire que d'utiliser! StringUtils.isNotBlank?
Kuchi
Cette réponse ne prend pas en compte les pièces jointes imbriquées en plusieurs parties (couramment utilisées par Thunderbird par exemple). Pour pouvoir trouver des pièces jointes en plusieurs parties imbriquées, voir @mefi answer.
Ruslan Stelmachenko
3
L'extrait de code est une faille de sécurité en attente de se produire. J'ai modifié l'extrait pour le mettre en évidence.
rzwitserloot
1
Bien sûr, et merci pour cela. Cet extrait était seulement pour démontrer comment cela peut être fait, naturellement ce n'est pas un code de production
David Rabinowitz
33

La question est très ancienne, mais peut-être qu'elle aidera quelqu'un. Je voudrais développer la réponse de David Rabinowitz.

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

ne doit pas renvoyer toutes les pièces jointes comme prévu, car vous pouvez avoir du courrier là où la partie mixte est sans disposition définie.

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

donc dans ce cas, vous pouvez également vérifier le nom du fichier. Comme ça:

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

ÉDITER

il y a tout le code de travail utilisant la condition décrite ci-dessus. Étant donné que chaque pièce peut encapsuler une autre pièce et que l'attachement doit être imbriqué, la récursivité est utilisée pour parcourir toutes les pièces

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
la source
l'expression vérifie que le nom de fichier est vide ou null. Est-ce exact?
Keerthivasan
Octopus: Oui. Vérifie si une CharSequence n'est pas vide (""), ni nulle ni d'espaces uniquement.
mefi
Hey pouvez-vous dire comment convertir la liste <InputStream> en liste <File>?
kumuda
kumunda: salut, vous devriez itérer cette liste et utiliser org.apache.commons.io.FileUtils.copyInputStreamToFile (source InputStream, destination de fichier) et chaque fichier ajouter à une autre collection. Si vous utilisez Guava, vérifiez Lists.transform (...), que vous pouvez utiliser au moment de l'itération (dépend de la façon dont vous devez initialiser chaque instance de fichier)
mefi
9

Un gain de temps pour le code dans lequel vous enregistrez le fichier de pièce jointe:

avec javax mail version 1.4 et ultérieure , vous pouvez dire

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

au lieu de

    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
la source
3
Apparemment, bodyPart devrait d'abord être converti en MimeBodyPart, comme:((MimeBodyPart) bodyPart).saveFile("/tmp/" + bodyPart.getFileName());
yair
5

Vous pouvez simplement utiliser l'API Apache Commons Mail MimeMessageParser - getAttachmentList () avec Commons IO et Commons Lang.

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
la source