如何序列化 Mimemessage 实例?

Posted

技术标签:

【中文标题】如何序列化 Mimemessage 实例?【英文标题】:How to serialize a Mimemessage instance? 【发布时间】:2013-07-10 15:35:42 【问题描述】:

我一直在尝试序列化 MimeMessage 实例,但正如我在网上阅读的那样,这是不可能的。我想通过序列化 MimeMessage 实例来实现的是,我想对该实例进行哈希处理并将其与邮件本身一起发送。到目前为止我的编码是这样的:

MimeMessage message = new MimeMessage(session);
//...setting up content of MimeMessage
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("object.ser")));
oos.writeObject(message);
oos.close();

它在 GlassFish 服务器上编译,但是当我尝试使用服务时出现运行时错误。它说:

exception

java.io.NotSerializableException: javax.mail.internet.MimeMessage

我试过这样做;但它也没有用:

Object obj = new Object();
obj = (Object)message;
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("object.ser")));
oos.writeObject(obj);
oos.close();

有什么方法可以实现对 MimeMessage 实例的序列化或以其他方式破解它?

【问题讨论】:

【参考方案1】:

实际上,MimeMessagedoes not implement Serializable by design,您可以扩展MimeMessage 来这样做,但您不需要因为MimeMessage 具有使用writeTo(OutputStream) 的功能,可以让您将内容保存为n RFC-822 mime消息。

try (OutputStream str = Files.newOutputStream(Paths.get("message.eml"))) 
    msg.writeTo(str);

然后您可以使用带有会话对象的MimeMessage(Session,InputStream) 构造函数读取此消息以供以后处理。

Session session = Session.getInstance(props);
try (InputStream str = Files.newInputStream(Paths.get("message.eml"))) 
    MimeMessage msg = new MimeMessage(session, str);
    // Do something with the message, maybe send it.
    Transport.send(msg);

如果你碰巧使用了spring的JavaMailSender,那么你也可以通过使用配置好的会话createMimeMessage(InputStream)来构造新的mime消息。

【讨论】:

在尝试发送 createMimeMessage(InputStream) 创建的邮件时,我得到了收件人的 NPE。反序列化后是否必须再次明确指定收件人?【参考方案2】:

NotSerializableException 在被序列化的对象没有实现java.io.Serializable 接口时被抛出。因为,javax.mail.internet.MimeMessage 没有实现这个接口,它不能被序列化。

public class MimeMessage extends Message implements MimePart

考虑将其内容序列化;通过将其包装在实现可序列化的自定义域对象(带有消息文本和收件人)中。需要时反序列化此域对象,然后继续从其内容构造一个新的 MimeMessage

【讨论】:

有没有让 MimeMessage 实现java.io.Serializable 接口,比如覆盖什么的?将 MimeMessage 对象转换为 ByteArray 是完全不可能的吗? (我假设序列化和转换成字节数组是一样的)。 当您尝试在 Java 中序列化对象时,必须序列化来自根的完整对象图。 MimeMessage 不可序列化的原因是因为它是不可序列化对象(如 Session)的容器,而 Session 又具有不可序列化的成员等。 而且,字节数组方法失败的原因是在反序列化时收到的邮件会话对象将无效(不活跃)。同样的概念可以防止数据库连接或数据流等资源被序列化。 感谢您的回答,先生。我打算散列 mimemessage 实例的字节数组。由于某种原因,我打算通过网络发送它。这就是我试图将 mimemessage 实例转换为字节数组的原因。现在我知道这是不可能的。【参考方案3】:

如其他答案所述:如果您不需要 MimeMessage 中的会话信息,则可以使用 MimeMessage.writeTo 方法将其存储在可序列化的包装对象中。作为模板,请参见以下代码(注意,它不是 Null 安全的)。

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.internet.MimeMessage;

import com.sun.mail.smtp.SMTPOutputStream;

public class SerializableMimeMessage implements Serializable 

    /**
     * 
     */
    private static final long serialVersionUID = 3763328805281033284L;

    private transient MimeMessage mimeMessage;

    public SerializableMimeMessage(MimeMessage mimeMessage) 
        this.mimeMessage = mimeMessage;
    

    private void writeObject(ObjectOutputStream oos) throws IOException 
        // convert
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        SMTPOutputStream os = new SMTPOutputStream(baos);
        try 
            mimeMessage.writeTo(os);
         catch (MessagingException e) 
            throw new IOException("MimeMessage could not be serialized.", e);
        
        os.flush();
        byte[] serializedEmail = baos.toByteArray();

        // default serialization 
        oos.defaultWriteObject();

        // write the object
        oos.writeInt(serializedEmail.length);
        oos.write(serializedEmail);
    

    private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException 
        // default deserialization
        ois.defaultReadObject();

        // read the object
        int len = ois.readInt();
        byte[] serializedEmail = new byte[len];
        ois.readFully(serializedEmail);

        // convert
        ByteArrayInputStream bais = new ByteArrayInputStream(serializedEmail);

        try 
            mimeMessage = new MimeMessage((Session) null, bais);
         catch (MessagingException e) 
            throw new IOException("MimeMessage could not be deserialized.", e);
        
    

    public MimeMessage getMimeMessage() 
        return mimeMessage;
    

【讨论】:

【参考方案4】:

这对我有用

序列化

            var mailMessage = new MimeMessage();
            mailMessage.From.Add(new MailboxAddress("from", "email@example.com"));
            mailMessage.To.Add(new MailboxAddress("to", "email2@example.com"));
            mailMessage.ReplyTo.Add(new MailboxAddress("reply", "email3@example.com"));
            mailMessage.Subject = "Test subject";
            var bodyBuilder = new BodyBuilder();
            bodyBuilder.TextBody = "GenericEmail";
            bodyBuilder.htmlBody = JsonConvert.SerializeObject(new Settings()  Exchange = "x" );
            mailMessage.Body = bodyBuilder.ToMessageBody();
            using var memoryStream = new MemoryStream();
            mailMessage.WriteTo(memoryStream);
            return memoryStream.ToArray();

反序列化

            using var stream = new MemoryStream(data);
            return MimeMessage.Load(stream);

请注意,我的用例是将电子邮件发送到 AMQP 队列,以便电子邮件发件人在可用或准备好时发送它。我的 AMQP 接收器处理电子邮件模板,因此我在 textbody 中发送模板,在 htmlbody 中发送变量

【讨论】:

以上是关于如何序列化 Mimemessage 实例?的主要内容,如果未能解决你的问题,请参考以下文章

如何序列化可能只有唯一实例的类

如何在 Django 中序列化模型实例?

如何序列化 CustomLineCap 类的实例

WCF反序列化如何在不调用构造函数的情况下实例化对象?

如何在 JSON 反序列化期间将引用转换为实例

如何在 WCF 中使用自定义序列化或反序列化来强制在 datacontact 的每个属性上创建一个新实例?