Descargar archivos adjuntos con Java Mail

96

Ahora que descargué todos los mensajes y los almacené en

Message[] temp;

¿Cómo obtengo la lista de archivos adjuntos para cada uno de esos mensajes?

List<File> attachments;

Nota: no libs de terceros, por favor, solo JavaMail.

folone
fuente

Respuestas:

109

Sin manejo de excepciones, pero aquí va:

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
fuente
2
Pero espere un minuto, ¿no se supone que debemos verificar si (bodyPart.getDisposition () == Part.ATTACHMENT) {} antes de guardar el archivo, para que no guarde el cuerpo del correo electrónico?
folone
8
¿No sería más natural leer StringUtils.isBlank () que usar! StringUtils.isNotBlank?
Kuchi
Esta respuesta no considera los archivos adjuntos de varias partes anidados (comúnmente utilizados por Thunderbird, por ejemplo). Para poder encontrar archivos adjuntos de varias partes anidados, consulte la respuesta de @mefi.
Ruslan Stelmachenko
3
El fragmento es una brecha de seguridad que está a punto de ocurrir. He editado el fragmento para resaltar esto.
rzwitserloot
1
Por supuesto, y gracias por eso. Este fragmento fue solo para demostrar cómo se puede hacer, naturalmente, no es un código de producción
David Rabinowitz
33

La pregunta es muy antigua, pero tal vez ayude a alguien. Me gustaría ampliar la respuesta de David Rabinowitz.

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

no debe devolver todos los adjuntos como espera, porque puede tener correo donde la parte mixta es sin una disposición definida.

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

en este caso, también puede verificar el nombre del archivo. Me gusta esto:

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

EDITAR

hay un código de trabajo completo que usa la condición descrita arriba. Debido a que cada parte puede encapsular otras partes y el adjunto debe estar anidado, la recursividad se usa para atravesar todas las partes

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
fuente
la expresión busca un nombre de archivo vacío o null. ¿Es eso correcto?
Keerthivasan
Pulpo: Sí. Comprueba si una CharSequence no está vacía (""), no es nula y no solo con espacios en blanco.
mefi
Oye, ¿puedes decirnos cómo convertir List <InputStream> en List <File>?
kumuda
kumunda: hola, debes iterar esa lista y usar org.apache.commons.io.FileUtils.copyInputStreamToFile (fuente InputStream, destino del archivo) y cada uno agregar archivo a otra colección. Si usa Guava, verifique Lists.transform (...), que puede usar en instad de iteración (depende de cómo necesite inicializar cada instancia de archivo)
mefi
9

Ahorro de tiempo para el código donde guarda el archivo adjunto:

con javax mail versión 1.4 y posteriores, puede decir

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

en vez 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
fuente
3
Aparentemente bodyPart debería convertirse primero a MimeBodyPart, como:((MimeBodyPart) bodyPart).saveFile("/tmp/" + bodyPart.getFileName());
yair
5

Simplemente puede usar Apache Commons Mail API MimeMessageParser - getAttachmentList () junto con Commons IO y 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
fuente