Pobierz załączniki za pomocą Java Mail

96

Teraz, gdy pobrałem wszystkie wiadomości i przechowuję je w

Message[] temp;

Jak uzyskać listę załączników dla każdej z tych wiadomości do

List<File> attachments;

Uwaga: żadnych bibliotek innych firm, tylko JavaMail.

folone
źródło

Odpowiedzi:

110

Bez obsługi wyjątków, ale oto:

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
źródło
2
Ale poczekaj chwilę, czy nie powinniśmy sprawdzać, czy (bodyPart.getDisposition () == Part.ATTACHMENT) {} przed zapisaniem pliku, aby nie zapisywał treści wiadomości e-mail?
folone
8
Czy czytanie StringUtils.isBlank () nie byłoby bardziej naturalne niż użycie! StringUtils.isNotBlank?
Kuchi
Ta odpowiedź nie uwzględnia zagnieżdżonych załączników wieloczęściowych (często używanych na przykład przez Thunderbirda). Aby móc znaleźć zagnieżdżone załączniki wieloczęściowe, zobacz @mefi answer.
Ruslan Stelmachenko
3
Fragment kodu oznacza naruszenie bezpieczeństwa, które czeka na wystąpienie. Dokonałem edycji fragmentu, aby to zaznaczyć.
rzwitserloot
1
Oczywiście i dzięki za to. Ten fragment miał tylko pokazać, jak można to zrobić, oczywiście nie jest to kod produkcyjny
David Rabinowitz,
33

Pytanie jest bardzo stare, ale może komuś pomoże. Chciałbym rozszerzyć odpowiedź Davida Rabinowitza.

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

nie powinien zwracać wszystkich załączników zgodnie z oczekiwaniami, ponieważ możesz mieć pocztę, w której część mieszana jest bez zdefiniowanej dyspozycji.

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

więc w tym przypadku możesz również sprawdzić nazwę pliku. Lubię to:

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

EDYTOWAĆ

istnieje cały działający kod wykorzystujący warunek opisany powyżej. Ponieważ każda część może zawierać inne części, a załącznik powinien być zagnieżdżony, do przechodzenia przez wszystkie części używana jest rekurencja

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
źródło
wyrażenie sprawdza, czy nazwa pliku jest pusta lub null. Czy to jest poprawne?
Keerthivasan
Ośmiornica: Tak. Sprawdza, czy CharSequence nie jest pusta (""), nie ma wartości null i nie zawiera tylko białych znaków.
mefi
Hej, czy możesz powiedzieć, jak przekonwertować List <InputStream> na List <File>?
kumuda
kumunda: cześć, powinieneś iterować tę listę i użyć org.apache.commons.io.FileUtils.copyInputStreamToFile (źródło InputStream, miejsce docelowe pliku) i każdy z nich dodać plik do innej kolekcji. Jeśli używasz Guavy, sprawdź Lists.transform (...), którego możesz użyć zamiast iteracji (zależy od tego, jak musisz zainicjować każdą instancję pliku)
mefi
9

Pewna oszczędność czasu dla kodu, w którym zapisujesz plik załącznika:

z pocztą javax w wersji 1.4 i późniejszych, można powiedzieć

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

zamiast

    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
źródło
3
Najwyraźniej bodyPart należy najpierw przekonwertować na MimeBodyParttakie jak:((MimeBodyPart) bodyPart).saveFile("/tmp/" + bodyPart.getFileName());
yair
5

Możesz po prostu użyć Apache Commons Mail API MimeMessageParser - getAttachmentList () wraz z Commons IO i 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
źródło