下面是网易邮箱的发送界面,我们也是基于这种常见的邮箱发送界面进行的OutBox界面设计:
而本项目中,发送邮件的OutBox最终GUI效果如下所示:
界面中有三个待填写的文本输入框,分别对应于收件人邮箱地址、邮件主题和邮件正文。左手边的三个按钮,从上至下的功能依次为:发送编辑好的邮件、退出OutBox和添加附件。退出该界面的代码实现比较容易,只需要使用Java-Swing中提供的API即可:
private void Exit()
{
int inquire = JOptionPane.showConfirmDialog(ClientSendPage.this,
"Sure to leave OutBox?","Leave OutBox.",
JOptionPane.YES_NO_OPTION);
if(inquire==JOptionPane.YES_OPTION)
{
this.dispose();
}
else
{
this.setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
}
}
SendButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
try
{
if(hasAttachment)
{
SendMailPro();
}
else
{
SendMail();
}
}
catch(Exception ex)
{
ex.printStackTrace();
}
}
});
由于我们这里的测试邮件是不带有附件的,所以会调用SendMail()方法进行发送。SendMail()方法中,首先需要进行环境的配置,包括邮件发送协议、邮件服务器的地址以及实际发送使用的端口号,我们前面的设计思路中曾经说过这是属于Session类实例对象的内容。本次项目中我们使用的是网易的邮箱客户端进行开发,所以实际的环境配置代码如下:
Properties pro = new Properties();
pro.put("mail.transport.protocol","smtp");
pro.put("mail.smtp.class","com.sun.mail.smtp.SMTPTransport");
pro.put("mail.smtp.host",SMTPServer);
/**SMTP port.*/
pro.put("mail.smtp.port","25");
/**Verify account.*/
pro.put("mail.smtp.auth","true");
session = Session.getInstance(pro, new Authenticator()
{
public PasswordAuthentication getPasswordAuthentication()
{
return new PasswordAuthentication(Account, Password);
}
});
transport = session.getTransport();
上述代码中,第一部分是完成属性的配置,然后封装成一个Session的对象;第二部分从这个Session对象中创建Transport的实例对象。需要注意的是此时,用户已经完成了邮件的编辑,而客户端已经完成了环境的配置,接下来客户端可以对用户编辑好的邮件数据进行封装了。同意是在设计思路中提到过,信息的封装也需要Session提供支持,而收件人地址、邮件主题以及邮件正文的内容,则可以从界面上的文本编辑框中轻松获得,封装信息的代码如下:
//Create a MimeMessage object.
MimeMessage message = new MimeMessage(NewSession);
//Set sender.
message.setFrom(new InternetAddress(Account));
//Set receiver.
message.setRecipients(Message.RecipientType.TO,
InternetAddress.parse(Receiver.getText()));
//Set Subject.
message.setSubject(Topic.getText());
//Set mail body.
message.setText(MailMessage.getText());
//SAVE CHANGES.
message.saveChanges();
其中一定不能忽视最后的saveChanges(),该方法用于保存并且生成最终的邮件内容。至此客户端已经完成了邮件的封装任务,下一步就是将其交付给已经获取到的Transport对象,进行传输了,代码如下:
transport.connect();
transport.sendMessage(message, message.getAllRecipients());
到这里,客户端已经完成了从配置环境,到封装邮件信息,再到最后的实际发送邮件的任务,接下来只需要在邮件发送成功后,给用户一个发送成功的信息即可。
稍后我们可以登录到实际的收件方邮箱中查看,是否本项目的第三方客户端真的发送了我们编辑的邮件。下图是Esperanto1949@163.com收件箱中实际收到的邮件内容,可以比较客户端上显示的发送时间和实际收到的邮件的发送时间,确认是同一份邮件。
message.setText(MailMessage.getText());
不同的是,这一次我们不仅仅有Text,我们还有附件。我们使用JavaMail中的MimeMultipart来表示一份带有附件的复杂邮件的主体部分,我们依次向其中添加邮件的正文以及附件(如果有多个的话)。
MIME消息的头字段Content-Type有三种类型:multipart/mixed、multipart/related、multipart/alternative(一封MIME邮件中的MIME消息可以有这三种组合关系).
前面的部分都与简单邮件的封装一致,需要重新编写代码的就是有关封装附件数据的部分,代码展示如下:
//Get mail body text.
MimeBodyPart ContentPart = CreateContent(MailMessage.getText());
//Create mixed MimeMultipart object.
MimeMultipart AllMultiPart =new MimeMultipart("mixed");
//Add mail body text.
AllMultiPart.addBodyPart(ContentPart);
//Add attachments in FileList.
for(int i=0;i<FileList.size();++i)
{
AllMultiPart.addBodyPart(FileList.get(i));
}
//setContent() & saveChanges().
message.setContent(AllMultiPart);
message.saveChanges();
关于用户如何选择附件的问题,我们需要用到Java中的JFileChooser,维护一个文件队列来进行多个被选中附件的记录。这部分的代码如下:
private void AppendAttachment() throws Exception
{
JFileChooser FileChooser = new JFileChooser();
if(FileChooser.showOpenDialog(ClientSendPage.this)==JFileChooser.APPROVE_OPTION)
{
String FileAddr = FileChooser.getSelectedFile().getCanonicalPath();
if(FileAddr!=null&&FileAddr.length()!=0)
{
hasAttachment=true;
FileName.add(FileAddr);
}
}
}
MailList = new JComboBox();
MailList.setBounds(740, 30, 180, 50);
MailList.setMaximumRowCount(5);
for(int i=0;i<number;++i)
{
MailList.addItem("Mail-No."+(i+1));
}
MailList.setSelectedIndex(0);
这里的两个方法setMaximunRowCount(int x)是指下列的视图中最多显示几个完整的item,我们从上面的下拉框效果图中也可以看出这一点,而setSelectedIndex(int x)则是设置默认选中的item的序列号(从0开始).
setEditable(false)
即可。查看邮件内容的代码展示如下://Open floder with 'READ_WRITE' right.
Folder folder = store.getFolder("inbox");
folder.open(Folder.READ_WRITE);
//Create MimeMessage object.
int Mail_Index = MailList.getSelectedIndex();
MimeMessage ThisMessage = (MimeMessage)((folder.getMessages())[Mail_Index]);
//Set sender.
String sender = String.valueOf((ThisMessage.getFrom())[0]);
from.setText(sender);
//Set topic.
topic.setText(ThisMessage.getSubject());
//Set text.
String textBody = String.valueOf(ThisMessage.getContent());
body.setText(textBody);
//Clse floder.
folder.close(true);
下图是当前Megatron1949@163.com邮箱中的第1封邮件的内容,后续我们通过InBox来查看这封邮件的内容作为对比:
if(hasAttachment(ThisMessage))
{
StringBuffer textbody = new StringBuffer();
//Get text content.
GetTextBody(ThisMessage,textbody);
body.setText(textbody.toString()+"\n\nNOTE:This mail has ATTACHMENT.");
}
else
{
String textBody = String.valueOf(ThisMessage.getContent());
body.setText(textBody);
}
private StringBuffer GetTextBody(Part part,StringBuffer textbody) throws Exception
{
boolean hasTextAttach = part.getContentType().indexOf("name")>0;
//text:Append directly.
if(part.isMimeType("text/*")&&!hasTextAttach)
{
textbody.append(part.getContent().toString());
}
//message:getContent().
else if(part.isMimeType("message/rfc822"))
{
GetTextBody((Part)part.getContent(),textbody);
}
//multipart:get every part.
else if(part.isMimeType("multipart/*"))
{
Multipart multipart = (Multipart)part.getContent();
int partCount = multipart.getCount();
for(int i=0;i<partCount;++i)
{
BodyPart bodypart = multipart.getBodyPart(i);
GetTextBody(bodypart, textbody);
}
}
return textbody;
}
private boolean hasAttachment(Part part)throws Exception
{
boolean has = false;
if(part.isMimeType("multipart/*"))
{
MimeMultipart multipart = (MimeMultipart)part.getContent();
int partCount = multipart.getCount();
for(int i=0;i<partCount;++i)
{
BodyPart bodyPart = multipart.getBodyPart(i);
String disp = bodyPart.getDisposition();
if(disp!=null&&
(disp.equalsIgnoreCase(Part.ATTACHMENT)||
disp.equalsIgnoreCase(Part.INLINE)))
{
has = true;
}
else if(bodyPart.isMimeType("multipart/*"))
{
has = hasAttachment(bodyPart);
}
else
{
String contentType = bodyPart.getContentType();
if(contentType.indexOf("application")!=-1)
{
has = true;
}
if(contentType.indexOf("name")!=-1)
{
has = true;
}
}
if(has)
{
break;
}
}
}
else if(part.isMimeType("message/rfc822"))
{
has = hasAttachment((Part)part.getContent());
}
return has;
}
这段代码中,我们首先针对那些是MimeMessage类型的邮件,这是第一个if条件表达式要求匹配到multipart/*
的结果。进入if分支后说明这一邮件由多个BodyPart组成,我们依次考察其中的每一个BodyPart。后续我们对于每一个BodyPart中的Disposition字段进行判断,该字段的值可以是null、ATTACHMENT或者INLINE.后两个预定义值在Java官方文档中的解释如下:
而getDisposition()方法在文档中的描述如下,该方法返回的是这一个part所被呈现出来的方式,ATTACHMENT表示它应该被当作附件呈现出来,而INLINE表示它应该被当作某种文本直接显示出来,而null则代表不知道。
所以这部分代码我自己的想法(如果有错,欢迎指出)是,只要有一个BodyPart的Disposition字段指明了它想要的呈现方式,无论是ATTACHMENT还是INLINE,我们都认为这封邮件中含有了附件,所以让has的值为true;后续如果该BodyPart还是一个复杂的multipart,我们就对其递归地调用hasAttachment()方法;而如果该BodyPart既不是multipart,它的Disposition字段也没有指明,我们就获取它的ContentTyep字段(在Http协议消息头中,使用ContentType来表示具体请求中的媒体类型信息),Java文档中对于getContentType()方法的描述如下:
当中提到了MIME typing system,关于MIME类型的完整列举,可以参看MIME参考。在这部分代码中,我们使用getContentType()方法获得了表示BodyPart的MIME类型的字符串,而后我们在该字符串中查找"application"和"name"两个字串,如果存在,就认为该消息中含有附件,令has的值为true。回到最外层的if分支,如果消息的类型是"message/rfc822",我们就对该消息的内容调用hasAttackment()方法。
private void SaveFile(InputStream is,String savePosition,String fileName) throws Exception
{
BufferedInputStream bis = new BufferedInputStream(is);
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(
new File(savePosition+fileName)));
int index=-1;
while((index=bis.read())!=-1)
{
bos.write(index);
bos.flush();
}
bos.close();
bis.close();
}
SaveFile的策略是一头连接着输入流,也就是待下载的附件,另一头连接着本地的某个文件位置,向其中写入数据。下图是一封带有附件的邮件,我们给出第三方客户端和网易邮箱的实际收件箱中的页面,保证该附件是确实存在的。
点击下载附件的JButton,再查看预先指定的保存位置,就能看到被下载好的附件。
private void DeleteMail(int index) throws Exception
{
Folder folder = store.getFolder(folderName);
if(folder==null)
{
throw new Exception(folderName+" does not exist.");
}
folder.open(Folder.READ_WRITE);
int inquire = JOptionPane.showConfirmDialog(ClientCheckPage.this,
"Sure to delete Mail-No."+(index+1)+" ?","Delete Mail.",
JOptionPane.YES_NO_OPTION);
if(inquire==JOptionPane.YES_OPTION)
{
Message DeleteMessage = (folder.getMessages())[index];
DeleteMessage.setFlag(Flags.Flag.DELETED, true);
JOptionPane.showMessageDialog(ClientCheckPage.this, "Mail-No."+(index+1)+
" has been deleted.");
}
else
{
this.setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
}
folder.close(true);
}
需要注意的是,最后一定要执行folder.close(true)
,否则表示收件箱的folder对象无法使删除操作生效。现在我们删除掉Megatron邮箱中的第5份邮件,再点击刷新JButton:
此时登录到网易邮箱的页面查看,发现该邮件确实已经被删除:
本文地址:https://blog.csdn.net/weixin_44246009/article/details/107464872
如对本文有疑问, 点击进行留言回复!!
sap cloud platform destination的配置
springcloud中feign调用处理mybatis-plus Ipage反序列化问题。
Flume 史上最全面的大数据学习第十篇(一) 别再说不知道flume是什么了
网友评论