我正在尝试使用 mime4j 解析电子邮件,一切正常,但是我无法获取附件的文件名。不幸的是,BodyDescriptor没有在内容处置或内容类型字段中包含此信息。
我已经读过MaximalBodyDescriptor将包含文件名,但是我不知道如何告诉解析器返回一个 MaximalBodyDescriptor 对象。
我的处理程序正在实现ContentHandler接口。我看不到可以工作的替代界面。
任何建议表示赞赏。
我正在尝试使用 mime4j 解析电子邮件,一切正常,但是我无法获取附件的文件名。不幸的是,BodyDescriptor没有在内容处置或内容类型字段中包含此信息。
我已经读过MaximalBodyDescriptor将包含文件名,但是我不知道如何告诉解析器返回一个 MaximalBodyDescriptor 对象。
我的处理程序正在实现ContentHandler接口。我看不到可以工作的替代界面。
任何建议表示赞赏。
Here is a helper class that we use successfully to parse e-mails with their attachments. In this approach attach.getFileName() has the filename.
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) {
}
}
我建议您使用令牌流。
这很简单。
您可以使用多部分的标题来定位附件:
Content-Disposition:attachment; filename="toto.txt"
用它解析标题时必须小心......它可以是邮件标题或多部分标题......