Unduh lampiran menggunakan Java Mail

96

Sekarang saya telah mengunduh semua pesan, dan menyimpannya ke

Message[] temp;

Bagaimana cara mendapatkan daftar lampiran untuk setiap pesan tersebut

List<File> attachments;

Catatan: tidak ada lib pihak ketiga, hanya JavaMail.

folone
sumber

Jawaban:

110

Tanpa penanganan pengecualian, tapi begini:

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
sumber
2
Tapi tunggu sebentar, bukankah kita harus memeriksa apakah (bodyPart.getDisposition () == Part.ATTACHMENT) {} sebelum menyimpan file, sehingga itu tidak menyimpan isi email?
folone
8
Bukankah StringUtils.isBlank () lebih natural untuk dibaca daripada menggunakan! StringUtils.isNotBlank?
Kuchi
Jawaban ini tidak mempertimbangkan lampiran multi bagian bersarang (biasanya digunakan oleh Thunderbird misalnya). Untuk dapat menemukan lampiran multi bagian bertingkat, lihat jawaban @mefi.
Ruslan Stelmachenko
3
Cuplikan tersebut adalah pelanggaran keamanan yang menunggu untuk terjadi. Saya telah mengedit cuplikan untuk menyorot ini.
rzwitserloot
1
Tentu saja, dan terima kasih untuk itu. Cuplikan ini hanya untuk mendemonstrasikan bagaimana hal itu dapat dilakukan, secara alami ini bukan kode produksi
David Rabinowitz
33

Pertanyaan sudah sangat tua, tapi mungkin itu akan membantu seseorang. Saya ingin memperluas jawaban David Rabinowitz.

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

tidak boleh mengembalikan semua lampiran seperti yang Anda harapkan, karena Anda dapat memiliki email di mana bagian campuran tanpa disposisi yang ditentukan.

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

jadi dalam kasus ini, Anda juga dapat memeriksa nama file. Seperti ini:

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

EDIT

ada seluruh kode kerja menggunakan kondisi yang dijelaskan di atas .. Karena setiap bagian dapat merangkum bagian lain dan lampiran harus disarangkan, rekursi digunakan untuk melintasi semua bagian

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
sumber
ekspresi memeriksa nama file kosong atau null. Apakah itu benar?
Keerthivasan
Gurita: Ya. Memeriksa apakah CharSequence tidak kosong (""), bukan nol dan tidak hanya spasi.
mefi
Hei, bisakah Anda memberitahu cara mengubah List <InputStream> menjadi List <File>?
kumuda
kumunda: hai, Anda harus mengulang daftar itu dan menggunakan org.apache.commons.io.FileUtils.copyInputStreamToFile (sumber InputStream, Tujuan file) dan masing-masing menambahkan file ke koleksi lain. Jika Anda menggunakan Guava, periksa Lists.transform (...), yang dapat Anda gunakan instad iterasi (tergantung pada bagaimana Anda perlu menginisialisasi setiap instance File)
mefi
9

Beberapa penghemat waktu untuk kode tempat Anda menyimpan file lampiran:

dengan javax mail versi 1.4 dan setelahnya, bisa dibilang

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

dari pada

    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
sumber
3
Rupanya bodyPart harus dikonversi dulu ke MimeBodyPart, seperti:((MimeBodyPart) bodyPart).saveFile("/tmp/" + bodyPart.getFileName());
yair
5

Anda cukup menggunakan Apache Commons Mail API MimeMessageParser - getAttachmentList () bersama Commons IO dan 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
sumber