JavaMail 问题或错误?

Posted

技术标签:

【中文标题】JavaMail 问题或错误?【英文标题】:JavaMail Question or Bug? 【发布时间】:2010-10-10 14:17:47 【问题描述】:

我有一个关于 Java Mail 以及它如何与流一起使用的问题。在 Java Mail 1.4.1 中有一个MimeMessage constructor that accepts a stream。我的理解是我可以将一个流传递给这个构造函数,它会为我将它解析成一个 MimeMessage。我写了 2 个测试来证明这一点。第一个测试发送一个只包含部分多部分 MIME 消息的流。第二个测试发送包含 2 个完整的多部分 MIME 消息的流。两者都不像我预期的那样工作。第一个不抛出异常,第二个以某种方式将整个流读入单个消息。这是 Java Mail 中的错误还是我使用了错误类型的流?还是我错过了更大的东西?

有点长,测试代码如下:

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.mail.BodyPart;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Session;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.util.ByteArrayDataSource;

import junit.framework.TestCase;


public class mimeTest extends TestCase 

    public void testPartialMulitpartMessage() throws MessagingException, IOException 
    
        Properties props = new Properties();
        Session session = Session.getInstance(props, null);
        String testMsg1 = "test";
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

        // Step 1 - Create first MIME message
        MimeMessage mesg = new MimeMessage(session);
        Multipart mp = new MimeMultipart("mixed");
        //create a child part
        BodyPart bodyPart = new MimeBodyPart();
        bodyPart.setContent(testMsg1, "application/x-special");
        bodyPart.setHeader("Content-Length", String.valueOf(testMsg1.length()));
        DataSource ds = new ByteArrayDataSource(testMsg1, "application/x-special");
        bodyPart.setDataHandler(new DataHandler(ds));
        bodyPart.setHeader("Content-Transfer-Encoding", "8bit");
        // Add the child part to the multipart
        mp.addBodyPart(bodyPart);
        // Put the MultiPart into the Message
        mesg.setContent(mp);

        // Step 2 - write to a stream
        mesg.writeTo(byteArrayOutputStream);

        byte bytes[] = byteArrayOutputStream.toByteArray();
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes, 0, 10);
        BufferedInputStream bufferedInputStream = new BufferedInputStream(byteArrayInputStream);
        boolean thrown = false;
        try
        
            //Why does this not throw a messageexception.
            MimeMessage mesg2 = new MimeMessage(session, bufferedInputStream);
        
        catch(MessagingException me)
            thrown = true;
        

        if(!thrown) 
            assertTrue("Expected exception not thrown.", false);
        
    

    public void testMulitpleMulitpartMessages() throws MessagingException, IOException 
        Properties props = new Properties();
        Session session = Session.getInstance(props, null);
        String testMsg1 = "test";
        String testMsg2 = "test1";
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

        // Step 1 - Create first MIME message
        MimeMessage mesg = new MimeMessage(session);
        Multipart mp = new MimeMultipart("mixed");
        //create a child part
        BodyPart bodyPart = new MimeBodyPart();
        bodyPart.setContent(testMsg1, "application/x-special");
        bodyPart.setHeader("Content-Length", String.valueOf(testMsg1.length()));
        DataSource ds = new ByteArrayDataSource(testMsg1, "application/x-special");
        bodyPart.setDataHandler(new DataHandler(ds));
        bodyPart.setHeader("Content-Transfer-Encoding", "8bit");
        // Add the child part to the multipart
        mp.addBodyPart(bodyPart);
        // Put the MultiPart into the Message
        mesg.setContent(mp);

        // Step 2 - write to a stream
        mesg.writeTo(byteArrayOutputStream);

        // Step 3 - Create second MIME message
        MimeMessage mesg2 = new MimeMessage(session);
        mp = new MimeMultipart("mixed");
        //create a child part
        bodyPart = new MimeBodyPart();
        bodyPart.setContent(testMsg2, "application/x-special");
        bodyPart.setHeader("Content-Length", String.valueOf(testMsg2.length()));
        ds = new ByteArrayDataSource(testMsg2, "application/x-special");
        bodyPart.setDataHandler(new DataHandler(ds));
        bodyPart.setHeader("Content-Transfer-Encoding", "8bit");
        // Add the child part to the multipart 
        mp.addBodyPart(bodyPart);
        // Put the MultiPart into the Message
        mesg2.setContent(mp);

        // Step 4 - write to the same stream
        mesg2.writeTo(byteArrayOutputStream);

        // Step 6 - read the two messages back
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
        BufferedInputStream bufferedInputStream = new BufferedInputStream(byteArrayInputStream);
        List<MimeMessage> listMessages = new ArrayList<MimeMessage>();
        while (bufferedInputStream.available() > 0) 
            //http://java.sun.com/products/javamail/javadocs/javax/mail/internet/MimeMessage.html#MimeMessage(javax.mail.Session,%20java.io.InputStream)
            //The InputStream will be left positioned at the end of the data for the message.
            //WHY does this not work?  It reads the whole stream.
            mesg = new MimeMessage(session, bufferedInputStream);
            //output the message
            listMessages.add(mesg);
        

        assertEquals(2, listMessages.size());

        assertTrue(listMessages.get(0).equals(mesg));
        assertTrue(listMessages.get(1).equals(mesg2));
    

【问题讨论】:

【参考方案1】:

JavaMail 将读取整个流,可能会缓冲它。我同意@skaffman,但我的观点是,它只是在没有 cmets 的情况下进行了高度优化,这使得它很难使用。

MimeMessage.parse(InputStream) 方法就是您要处理的。它会读取整个流,而不关闭它,除非您使用的是 SharedInputStream。如您所见,它将使 InputStream 位于最后。

MimeMessage 对解释流中的数据没有多大作用,因此我认为你“错过了更大的东西”来使用你的话。你为什么期望它解释数据?您可以只发送用于创建流的多部分消息吗?

【讨论】:

【参考方案2】:

JavaMail API 是一头猪,直接使用它是一项吃力不讨好的任务,主要是因为它的行为与您期望的不一样。

我建议在它周围使用 Spring 的 API 层,它的压力要小得多。它并没有完全隐藏 JavaMail,它只是让它更易于预测和测试友好。

【讨论】:

以上是关于JavaMail 问题或错误?的主要内容,如果未能解决你的问题,请参考以下文章

javamail电子邮件不发送

javamail 错误,加上send就出错,求助

javamail的邮件发送有验证失败错误

javamail如何处理退信

JavaMail在Windows平台下正常发送邮件,部署到Linux后则发送失败

JavaMail直接发送邮件