相信很多小伙伴在使用JavaMail解析收件邮件时,会遇到邮件正文中的图片内容都被作为附件保存的问题,从而导致正文中的图片丢失,大大的影响了邮件阅读观感。 先简述一下解决思路
相信很多小伙伴在使用JavaMail解析收件邮件时,会遇到邮件正文中的图片内容都被作为附件保存的问题,从而导致正文中的图片丢失,大大的影响了邮件阅读观感。
先简述一下解决思路,正文中的图片,大都会以标签形式保存在原文中。例如下边图1所示,是QQ邮箱中收件人收到的邮件:
我们需要获取Content-ID,并将其对应在正文中的标签中原本的内容替换为我们最终的转换出的base64字符串。这封邮件的邮件源码的其中一部分如下边图二所示:
废话不多说,上代码(下边只展示重要的方法,对于包的引入缺啥引啥就好,没有什么特别的包)
private void parseMail(Message[] messages) throws Exception { for (int i = 0; i < messages.length; i++) { MailManagement theMail = new MailManagement(); System.out.println("------------------正在解析第" + i + "封邮件-------------------- "); //主题(防止主题为空) String subject = messages[i].getSubject() == null ? null : MimeUtility.decodeText(messages[i].getSubject()); System.out.println("主题: " + subject); //解析邮件发送时间,个别邮件会出现发送时间为空的状况 System.out.println("发送时间:" + (messages[i].getSentDate() != null ? new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(messages[i].getSentDate()) : null)); //发件人 String from = getFrom((MimeMessage) messages[i]); System.out.println("发件人:" + from); //收件人 String to = getReceiveAddress((MimeMessage) messages[i], Message.RecipientType.TO); System.out.println("收件人:" + to); //抄送人 String cc = getReceiveAddress((MimeMessage) messages[i], Message.RecipientType.CC); System.out.println("抄送人:" + cc); //解析邮件正文内容 MimeMessage msg = new MimeMessage ((MimeMessage) messages[i]); StringBuffer contentText = new StringBuffer(); Map<String, String> map = new HashMap<>(); String contentType = msg.getContentType(); if (contentType.startsWith("text/plain")) { getMailTextContent(msg,contentText,true,map); }else { getMailTextContent(msg,contentText,false,map); } String trueContent=contentText.toString(); //替换图片为base64字符串 for (String contentId : map.keySet()) { String rpStr="cid:"+contentId.replace("<","").replace(">",""); trueContent=trueContent.replace(rpStr,"data:image/jpeg;base64,"+map.get(contentId)); } System.out.println("邮件内容:"+trueContent); //判断是否拥有附件 boolean isContainerAttachment = isContainAttachment(msg); System.out.println("是否包含附件:" + isContainerAttachment); } } /** * 获取发件人的昵称和发件账号 */ public static String getFrom(MimeMessage mimeMessage) throws Exception { InternetAddress[] address = (InternetAddress[]) mimeMessage.getFrom(); if(ArrayUtils.isEmpty(address)){ return ""; } String from = address[0].getAddress(); if (from == null) { from = ""; } String personal = address[0].getPersonal(); if (personal == null) { personal = ""; } return personal + "<" + from + ">"; } /** * 根据收件人类型,获取邮件收件人、抄送和密送地址。如果收件人类型为空,则获得所有的收件人 * Message.RecipientType.TO 收件人 * Message.RecipientType.CC 抄送人 * Message.RecipientType.BCC 密送人 * @param msg 邮件内容 * @param type 收件人类型 * @return 收件人1 <邮件地址1>, 收件人2 <邮件地址2>, ... */ public String getReceiveAddress(MimeMessage msg, Message.RecipientType type) throws MessagingException { StringBuilder receiveAddress = new StringBuilder(); Address[] addresses; //指定抄送密送等类型 if (type == null) { addresses = msg.getAllRecipients(); } else { addresses = msg.getRecipients(type); } if (addresses == null || addresses.length < 1) { return ""; } for (Address address : addresses) { InternetAddress internetAddress = address; String from = internetAddress.getAddress(); if (from == null) { from = ""; } String personal = internetAddress.getPersonal(); if (personal == null) { personal = ""; } receiveAddress.append(personal).append("<").append(from).append(">").append(","); } //删除最后一个逗号 receiveAddress.deleteCharAt(receiveAddress.length()-1); return receiveAddress.toString(); }public static void getMailTextContent(Part part, StringBuffer str,boolean plainFlag,Map<String,String> map)throws MessagingException,IOException { boolean isContainTextAttach = part.getContentType().indexOf("name") > 0; //为了防止解析正文内容时间 //根据plainFlag标记来判断,如果为true那么就不再次从text/html中获取正文内容 if (part.isMimeType("text/html") && !isContainTextAttach && !plainFlag) { str.append(MimeUtility.decodeText(part.getContent().toString())); } else if (part.isMimeType("text/plain") && plainFlag) { str.append(part.getContent().toString()); //置为false plainFlag=false; } else if (part.isMimeType("message/rfc822")) { getMailTextContent((Part) part.getContent(), str,plainFlag,map); } 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); getMailTextContent(bodyPart, str,plainFlag,map); } } else if (part.isMimeType("image/*") && !"attachment".equals(part.getDisposition())) { System.out.println("Dispo是:"+part.getDisposition()); Object content = part.getContent(); String contentId=null; try { contentId=((String[]) part.getHeader("Content-ID"))[0]; } catch (MessagingException e) { System.err.println("未知格式"); } if(contentId!=null){ InputStream in=(InputStream) content; String toBase64=inputStreamToBase64(in); map.put(contentId,toBase64); } } } private static String inputStreamToBase64(InputStream in) { try { byte[] data=null; byte[] tempByte=new byte[100]; ByteArrayOutputStream bos= new ByteArrayOutputStream(); int rc=0; while((rc=in.read(tempByte,0,100))>0){ bos.write(tempByte,0,rc); } data=bos.toByteArray(); if(ArrayUtils.isEmpty(data)){ return ""; } return new String(Base64.getEncoder().encode(data)); } catch (IOException e) { e.printStackTrace(); return ""; }finally { if(in!=null){ try { in.close(); } catch (IOException e) { e.printStackTrace(); } } } } /** * 判断邮件中是否包含附件 * @param part 邮件中多个组合体中的其中一个组合体 * @return 邮件中存在附件返回true,不存在返回false */ public static boolean isContainAttachment(Part part) throws Exception { boolean flag = 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))) { flag = true; } else if (bodyPart.isMimeType("multipart/*")) { flag = isContainAttachment(bodyPart); } else { String contentType = bodyPart.getContentType(); if (contentType.contains("application")) { flag = true; } if (contentType.contains("name")) { flag = true; } } if (flag) { break; } } } else if (part.isMimeType("message/rfc822")) { flag = isContainAttachment((Part)part.getContent()); } return flag; }/** * 文本解码 * @param encodeText 解码MimeUtility.encodeText(String text)方法编码后的文本 * @return 解码后的文本 */ public static String decodeText(String encodeText) throws UnsupportedEncodingException, ParseException { if (encodeText == null || "".equals(encodeText)) { return ""; } else { String tochinese = tochinese(encodeText); return MimeUtility.decodeText(tochinese); } } public static String tochinese(String strvalue){ try{ if(strvalue==null) { return null; } else{ strvalue = new String(strvalue.getBytes("iso8859_1"), "gbk"); return strvalue; } }catch(Exception e){ return null; } }以上代码着重看parseMail()方法中的base64替换的过程,和getMailTextContent()方法即可。
在使用以上方法所解析好正文内容后,使用在线html编辑器预览一下我们所解析出的内容是否可以正常展示图片。具体效果如下面图三:
第一次写博客,写的可能不太好,望大家多多包涵!由于本人是在内网开发邮箱功能,所以并没有采用将正文图片上传至阿里云OSS等云服务器以获取图片在线url并替换原文中的图片cid标签,本文讲述的是将正文中的图片转换为base64格式并替换其原本在正文中的标签内容。如有需要,我后续可更新一版博客关于采用url的方式替换原文中的内嵌图片