使用mime4j检索电子邮件附件文件名

时间:2013-06-18 09:27:16

标签: java apache-tika mime4j

我正在尝试使用mime4j来解析电子邮件,一切正常,但是我无法获取附件的文件名。遗憾的是, BodyDescriptor 不会在内容处置或内容类型字段中包含此信息。

我已经读过 MaximalBodyDescriptor 将包含文件名,但我不知道如何告诉解析器返回MaximalBodyDescriptor对象。

我的处理程序正在实现 ContentHandler 界面。我看不到可以使用的备用接口。

任何建议表示赞赏。

2 个答案:

答案 0 :(得分:0)

这是一个帮助程序类,我们成功地使用它们的附件来解析电子邮件。 在这种方法中,attach.getFileName()具有文件名。

package com.bitplan.smartCRM;

import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.ClipboardOwner;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.mail.Flags;
import javax.mail.Folder;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Store;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

import org.apache.commons.io.IOUtils;
import org.apache.james.mime4j.MimeException;
import org.apache.james.mime4j.dom.Body;
import org.apache.james.mime4j.dom.Entity;
import org.apache.james.mime4j.dom.Message;
import org.apache.james.mime4j.dom.MessageBuilder;
import org.apache.james.mime4j.dom.MessageServiceFactory;
import org.apache.james.mime4j.dom.Multipart;
import org.apache.james.mime4j.dom.SingleBody;
import org.apache.james.mime4j.dom.TextBody;
import org.apache.james.mime4j.dom.address.MailboxList;
import org.apache.james.mime4j.message.MessageImpl;
import org.apache.james.mime4j.stream.Field;

import com.bitplan.restinterface.Configuration;

/**
 * EMail Helper class
 * 
 * @author wf
 * @author Denis Lunev <den@mozgoweb.com>
 * @see http 
 *      ://www.mozgoweb.com/posts/how-to-parse-mime-message-using-mime4j-library
 *      /
 */
public class EMailHelper implements ClipboardOwner {

    public static boolean debug = true;
    public static Logger LOGGER = Logger
            .getLogger("com.bitplan.common.EMailHelper");
    private StringBuffer txtBody;
    private StringBuffer htmlBody;
    private ArrayList<Entity> attachments;

    /**
     * get a String from an input Stream
     * 
     * @param inputStream
     * @return
     * @throws IOException
     */
    public String fromInputStream(InputStream inputStream) throws IOException {
        String result = IOUtils.toString(inputStream);
        // System.out.println(result);
        return result;
    }

    /**
     * get the full Mail from a message
     * 
     * @param message
     * @return
     * @throws MessagingException
     * @throws IOException
     */
    public String fullMail(javax.mail.Message message) throws MessagingException,
            IOException {
        StringBuffer sBuf = new StringBuffer();
        @SuppressWarnings("unchecked")
        Enumeration<javax.mail.Header> headers = message.getAllHeaders();
        while (headers.hasMoreElements()) {
            javax.mail.Header header = headers.nextElement();
            sBuf.append(header.getName() + ": " + header.getValue() + "\n");
        }
        sBuf.append(fromInputStream(message.getInputStream()));
        return sBuf.toString();
    }

    /**
     * Authentication
     */
    public static class Authentication {
        enum AuthenticationType {
            pop3, smtp
        };

        String host;
        String user;
        String password;
        AuthenticationType authenticationType;
        Transport mTransport;

        /**
         * create an Authentication from the configuration
         * 
         * @param configuration
         * @param pAuthType
         */
        public Authentication(Configuration configuration,
                AuthenticationType pAuthType) {
            authenticationType = pAuthType;
            String prefix = pAuthType.name() + ".";
            // use prefix e.g. pop3.host / smtp.host
            host = (String) configuration.toMap().get(prefix + "host");
            user = (String) configuration.toMap().get(prefix + "user");
            password = (String) configuration.toMap().get(prefix + "password");
        }

        /**
         * authenticate for sending / receiving e-mail
         * 
         * @throws MessagingException
         */
        public Transport authenticate() throws MessagingException {
            Properties lProps = new Properties();
            Session session = Session.getDefaultInstance(lProps);
            switch (authenticationType) {
            case pop3:
                Store store = session.getStore("pop3");
                store.connect(host, user, password);
                store.close();
                return null;
            case smtp:
                // http://javamail.kenai.com/nonav/javadocs/com/sun/mail/smtp/package-summary.html
                mTransport = session.getTransport("smtp");
                mTransport.connect(host, user, password);
                return mTransport;
            }
            return null;
        }
    }

    /**
     * send the given e-mail
     * 
     * @param email
     * @throws MessagingException
     */
    public void send(EMail email, Configuration configuration)
            throws MessagingException {
        Authentication lAuth = new Authentication(configuration,
                Authentication.AuthenticationType.pop3);
        Properties lProps = System.getProperties();
        lProps.put("mail.smtp.host", lAuth.host);
        // WF 2004-09-18: make sure full qualified domain name is used for localhost
        // the default InetAddress.getLocalHost().getHostName() might not work ...
        // lProps.put("mail.smtp.localhost",java.net.InetAddress.getLocalHost().getCanonicalHostName());
        Session lSession = Session.getInstance(lProps);
        MimeMessage lMsg = new MimeMessage(lSession);
        lMsg.setFrom(new InternetAddress(email.getFromAdr()));
        lMsg.setRecipients(javax.mail.Message.RecipientType.TO,
                InternetAddress.parse(email.getToAdr()));
        if (email.getCC() != null)
            lMsg.setRecipients(javax.mail.Message.RecipientType.CC,
                    InternetAddress.parse(email.getCC()));
        /*
         * if (bcc()!=null) lMsg.setRecipients(Message.RecipientType.BCC,
         * InternetAddress.parse(bcc())); lMsg.setHeader("X-Mailer", "JavaMail");
         * lMsg.setSentDate(new Date()); lMsg.setSubject(subject());
         * lMsg.setText(content()); lMsg.saveChanges(); Transport
         * lTransport=lAuth.authenticate(); if (lTransport!=null)
         * lTransport.sendMessage(lMsg,lMsg.getAllRecipients()); } else {
         * Transport.send(lMsg); }
         */
    }

    /**
     * retrieve pop3 mail from the given host
     * 
     * @param pop3host
     * @param user
     * @param password
     * @throws Exception
     */
    public List<EMail> retrievePop3Mail(EMailManager eMailmanager,
            Configuration configuration) throws Exception {
        List<EMail> result = new ArrayList<EMail>();
        Properties lProps = new Properties();
        Session session = Session.getDefaultInstance(lProps);
        Store store = session.getStore("pop3");
        File attachmentDirectory = (File) configuration.toMap().get(
                "attachmentDirectory");
        // get a pop3 authentication
        Authentication auth = new Authentication(configuration,
                Authentication.AuthenticationType.pop3);
        store.connect(auth.host, auth.user, auth.password);

        Folder remoteInbox = store.getFolder("INBOX");
        remoteInbox.open(Folder.READ_WRITE);
        javax.mail.Message message[] = remoteInbox.getMessages();
        if (message.length > 0) {
            // get all messages
            LOGGER.log(Level.INFO, "Getting " + message.length
                    + " messages from POP3 Server '" + store.getURLName() + "'");
            for (int i = 0; i < message.length; i++) {
                if (!message[i].isSet(Flags.Flag.DELETED)) {
                    EMail email = eMailmanager.create();
                    String mailInput = this.fullMail(message[i]);
                    // System.out.print(mailInput);
                    ByteArrayInputStream mailStream = new ByteArrayInputStream(
                            mailInput.getBytes());
                    this.parseMessage(email, mailStream, attachmentDirectory);
                    result.add(email);
                    message[i].setFlag(Flags.Flag.DELETED, true);
                }
            } // for
        } // if
        remoteInbox.close(true);
        store.close();
        return result;
    }

    /**
     * parse the Message into the given EMail
     * 
     * @param email
     * @param fileName
     * @param attachmentDirectory
     * 
     * @throws Exception
     */
    public void parseMessage(EMail email, String fileName,
            String attachmentDirectory) throws Exception {
        parseMessage(email, new File(fileName), new File(attachmentDirectory));
    }

    /**
     * strip the brackets
     * 
     * @param addressList
     * @return
     */
    public String stripBrackets(MailboxList addressList) {
        String result = null;
        if (addressList != null) {
            result = addressList.toString();
            if (result.startsWith("[") && result.endsWith("]")) {
                result = result.substring(1, result.length() - 1);
            }
        }
        return result;
    }

    /**
     * parse the Message from the given file into the given e-mail using the given
     * attachmentDirectory
     * 
     * @param email
     * @param file
     * @param attachmentDirectory
     * @throws Exception
     */
    public void parseMessage(EMail email, File file, File attachmentDirectory)
            throws Exception {
        if (!file.canRead() || (!file.isFile()))
            throw new IllegalArgumentException(file.getCanonicalPath()
                    + " is not a readable file");
        // Get stream from file
        FileInputStream fis = new FileInputStream(file);
        parseMessage(email, fis, attachmentDirectory);
    }

    /**
     * parse the Message from the given file into the given e-mail using the given
     * attachmentDirectory
     * 
     * @param email
     * @param emailInputStream
     * @param attachmentDirectory
     * @throws Exception
     */
    public void parseMessage(EMail email, InputStream eMailInputStream,
            File attachmentDirectory) throws Exception {

        Message mimeMsg = null;
        if (!attachmentDirectory.isDirectory())
            throw new IllegalArgumentException(attachmentDirectory.getCanonicalPath()
                    + " is not a directory");

        txtBody = new StringBuffer();
        htmlBody = new StringBuffer();
        attachments = new ArrayList<Entity>();
        Exception ex = null;
        try {
            // Create message with stream from file
            // If you want to parse String, you can use:
            // Message mimeMsg = new Message(new
            // ByteArrayInputStream(mimeSource.getBytes()));
            MessageServiceFactory factory = MessageServiceFactory.newInstance();
            MessageBuilder msgBuilder = factory.newMessageBuilder();
            try {
                mimeMsg = msgBuilder.parseMessage(eMailInputStream);
            } catch (Throwable th) {
                LOGGER.log(Level.SEVERE,th.getClass().getName());
                LOGGER.log(Level.SEVERE,th.getMessage());
            }
            if (mimeMsg == null) {
                LOGGER.log(Level.SEVERE, "could not read mime msg:\n",
                        this.fromInputStream(eMailInputStream));
                return;
            }
            // Get some standard headers
            if (mimeMsg.getTo() != null)
                email.setToAdr(stripBrackets(mimeMsg.getTo().flatten()));
            email.setFromAdr(stripBrackets(mimeMsg.getFrom()));
            email.setSubject(mimeMsg.getSubject());
            email.setSendDate(mimeMsg.getDate());
            email.setEMailId(mimeMsg.getMessageId());
            LOGGER.log(Level.INFO, "To: " + email.getToAdr());
            LOGGER.log(Level.INFO, "From: " + email.getFromAdr());
            LOGGER.log(Level.INFO, "Subject: " + mimeMsg.getSubject());

            // Get custom header by name
            Field priorityFld = mimeMsg.getHeader().getField("X-Priority");
            // If header doesn't found it returns null
            if (priorityFld != null) {
                // Print header value
                LOGGER.log(Level.FINEST, "Priority: " + priorityFld.getBody());
            }

            // If message contains many parts - parse all parts
            if (mimeMsg.isMultipart()) {
                Multipart multipart = (Multipart) mimeMsg.getBody();
                parseBodyParts(multipart);
                // fix mime4j 0.7.2 behaviour to have no separate text/plain part
                if (txtBody.length() == 0) {
                    txtBody.append(multipart.getPreamble());
                }
            } else {
                // If it's single part message, just get text body
                String text = getTxtPart(mimeMsg);
                txtBody.append(text);
            }
            email.setContent(txtBody.toString());

            // Print text and HTML bodies
            if (debug) {
                LOGGER.log(Level.FINEST, "Text body: " + txtBody.toString());
                LOGGER.log(Level.FINEST, "Html body: " + htmlBody.toString());
            }
            // loop over attachments
            for (Entity attach : attachments) {
                writeAttachment(attach, attachmentDirectory);
            }
        } catch (Exception cex) {
            ex = cex;
        } finally {
            if (eMailInputStream != null) {
                try {
                    eMailInputStream.close();
                } catch (IOException ioex2) {
                    ioex2.printStackTrace();
                }
            }
        }
        if (ex != null) {
            throw ex;
        }
    }

    /**
     * write the given Attachment
     * 
     * @param attach
     * @param attachmentDirectory
     * @throws IOException
     */
    public void writeAttachment(Entity attach, File attachmentDirectory)
            throws IOException {
        String attName = attach.getFilename();
        // Create file with specified name
        if (attName == null) {
            LOGGER.log(Level.WARNING, "attachment has no file name using 'attachment"
                    + attach.hashCode() + "' instead");
            attName = "attachment" + attach.hashCode();
        }

        FileOutputStream fos = new FileOutputStream(new File(attachmentDirectory,
                attName));
        try {
            writeBody(fos, attach.getBody());
        } finally {
            fos.close();
        }
    }

    /**
     * write the given body to the given fileoutput stream
     * 
     * @param fos
     * @param body
     * @throws IOException
     */
    public void writeBody(FileOutputStream fos, Body body) throws IOException {
        if (body instanceof SingleBody) {
            ((SingleBody) body).writeTo(fos);
        } else if (body instanceof MessageImpl) {
            writeBody(fos, ((MessageImpl) body).getBody());
        } else {
            LOGGER.log(Level.WARNING, "can't handle body of type "
                    + body.getClass().getSimpleName());
        }
    }

    /**
     * This method classifies bodyPart as text, html or attached file
     * 
     * @param multipart
     * @throws IOException
     */
    private void parseBodyParts(Multipart multipart) throws IOException {
        // loop over the parts
        for (Entity part : multipart.getBodyParts()) {
            String mimeType = part.getMimeType();
            if (mimeType.equals("text/plain")) {
                String txt = getTxtPart(part);
                txtBody.append(txt);
            } else if (mimeType.equals("text/html")) {
                String html = getTxtPart(part);
                htmlBody.append(html);
            } else if (part.getDispositionType() != null
                    && !part.getDispositionType().equals("")) {
                // If DispositionType is null or empty, it means that it's multipart,
                // not attached file
                attachments.add(part);
            }

            // If current part contains other, parse it again by recursion
            if (part.isMultipart()) {
                parseBodyParts((Multipart) part.getBody());
            }
        }
    }

    /**
     * 
     * @param part
     * @return
     * @throws IOException
     */
    private String getTxtPart(Entity part) throws IOException {
        // Get content from body
        TextBody tb = (TextBody) part.getBody();
        return this.fromInputStream(tb.getInputStream());
    }

    /**
     * Place a String on the clipboard, and make this class the owner of the
     * Clipboard's contents.
     * 
     * @param aString
     */
    public void setClipboardContents(String aString) {
        StringSelection stringSelection = new StringSelection(aString);
        Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
        clipboard.setContents(stringSelection, this);
    }

    /**
     * get text from the clipboard
     * 
     * @return
     * @throws Exception
     */
    public String getClipboardText() throws Exception {
        Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
        String text = (String) clipboard.getData(DataFlavor.stringFlavor);
        return text;
    }

    /**
     * get Mail from clipboard
     * 
     * @param email
     * @param attachmentDirectory
     * @return
     * @throws Exception
     */
    public boolean getMailFromClipboard(EMail email, File attachmentDirectory)
            throws Exception {
        String mailText = getClipboardText();
        if (mailText == null)
            return false;
        this.parseMessage(email,
                new ByteArrayInputStream(mailText.getBytes("UTF-8")),
                attachmentDirectory);
        return true;
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * java.awt.datatransfer.ClipboardOwner#lostOwnership(java.awt.datatransfer
     * .Clipboard, java.awt.datatransfer.Transferable)
     */
    @Override
    public void lostOwnership(Clipboard clipboard, Transferable contents) {
    }

}

答案 1 :(得分:0)

我建议您使用Token streams

它很直接。

您可以使用多部分部分的标题找到附件:

  Content-Disposition:attachment; filename="toto.txt"

使用它解析标题时必须小心......它可以是邮件标题或多部分标题....