JavaMail发送邮件,附件中文乱码原因解析及解决方案

您所在的位置:网站首页 pop中文名称 JavaMail发送邮件,附件中文乱码原因解析及解决方案

JavaMail发送邮件,附件中文乱码原因解析及解决方案

2023-03-03 06:46| 来源: 网络整理| 查看: 265

背景

使用的依赖如下: org.springframework.boot:spring-boot-starter-mail -> 2.2.1.RELEASE 该依赖下用于发送邮件的jar包如下: org.springframework:spring-context-support:5.2.1.RELEASE com.sun.mail:jakarta.mail:1.6.4

如果你的项目中引入了com.sun.mail:javax.mail:1.5.2及之前的版本,你会发现并不会出现附件中文乱码,当使用了com.sun.mail:javax.mail:1.5.3以上的版本或者使用jakarta.mail的版本,就会出现附件中文乱码。 注意:javax.mail最新版截至1.6.2,之后启用jakarta.mail,其版本从1.6.3开始

原因剖析及解决方案

作者在网上找到了很多对该问题的解决方案的文章,但是都没有对该问题的出现原因进行分析。下面我就通过源码对该问题做个简单的解释,文章基于你已经完成了邮件的发送。

// 这里设置编码,如果没有设置,也会出现中文乱码,不过这个是最基本的 MimeMessageHelper messageHelper = new MimeMessageHelper(mimeMessage, true, "UTF-8"); //这里添加附件,fileName文件名称,fileSystemResource为附件 messageHelper.addAttachment(fileName, fileSystemResource);

上面的代码是我们发送邮件添加附件的基本操作,问题就出现在添加附件时,我们进入源码

/** * org.springframework.mail.javamail.MimeMessageHelper#addAttachment(java.lang.String, org.springframework.core.io.InputStreamSource) */ public void addAttachment(String attachmentFilename, InputStreamSource inputStreamSource) throws MessagingException { String contentType = getFileTypeMap().getContentType(attachmentFilename); addAttachment(attachmentFilename, inputStreamSource, contentType); } public void addAttachment( String attachmentFilename, InputStreamSource inputStreamSource, String contentType) throws MessagingException { ... DataSource dataSource = createDataSource(inputStreamSource, contentType, attachmentFilename); addAttachment(attachmentFilename, dataSource); } public void addAttachment(String attachmentFilename, DataSource dataSource) throws MessagingException { ... try { MimeBodyPart mimeBodyPart = new MimeBodyPart(); mimeBodyPart.setDisposition(MimeBodyPart.ATTACHMENT); //问题就出现在这里 mimeBodyPart.setFileName(MimeUtility.encodeText(attachmentFilename)); mimeBodyPart.setDataHandler(new DataHandler(dataSource)); getRootMimeMultipart().addBodyPart(mimeBodyPart); } catch (UnsupportedEncodingException ex) { throw new MessagingException("Failed to encode attachment filename", ex); } }

可以看到最终是在addAttachment方法中调用mimeBodyPart.setFileName来设计文件名称,参数是对输入的文件名进行编码。进入mimeBodyPart.setFileName

static void setFileName(MimePart part, String name) throws MessagingException { ... // Set the Content-Disposition "filename" parameter String s = part.getHeader("Content-Disposition", null); ContentDisposition cd = new ContentDisposition(s == null ? Part.ATTACHMENT : s); cd.setParameter("filename", name); part.setHeader("Content-Disposition", cd.toString()); ... }

可以看到这里是在header中设置Content-Disposition参数,也就是真正设置文件名称的地方,最终调用了ContentDisposition的toString方法

private ParameterList list; public String toString() { if (primaryType == null || subType == null) // need both return ""; StringBuffer sb = new StringBuffer(); sb.append(primaryType).append('/').append(subType); if (list != null) //在setFileName时 设置parameter,这里的list就是这个参数,可以理解list存储的就是文件名 //细节可以自己研究,这里不做深入,也就是最终调用list.toString方法 sb.append(list.toString(sb.length() + 14)); return sb.toString(); }

终于进入正题 ParameterList.toString方法,1.5.2版本之前如下:

public String toString(int used) { ToStringBuffer sb = new ToStringBuffer(used); Iterator e = list.keySet().iterator(); while (e.hasNext()) { String name = (String)e.next(); Object v = list.get(name); if (v instanceof MultiValue) { MultiValue vv = (MultiValue)v; String ns = name + "*"; for (int i = 0; i ToStringBuffer sb = new ToStringBuffer(used); Iterator e = list.entrySet().iterator(); while (e.hasNext()) { Map.Entry ent = e.next(); String name = ent.getKey(); String value; Object v = ent.getValue(); if (v instanceof MultiValue) { MultiValue vv = (MultiValue)v; name += "*"; for (int i = 0; i ns = name + i + "*"; value = ((Value)va).encodedValue; } else { ns = name + i; value = (String)va; } sb.addNV(ns, quote(value)); } } else if (v instanceof LiteralValue) { value = ((LiteralValue)v).value; sb.addNV(name, quote(value)); } else if (v instanceof Value) { /* * XXX - We could split the encoded value into multiple * segments if it's too long, but that's more difficult. */ name += "*"; value = ((Value)v).encodedValue; sb.addNV(name, quote(value)); } else { value = (String)v; if (value.length() > 60 && splitLongParameters && encodeParameters) { int seg = 0; name += "*"; while (value.length() > 60) { sb.addNV(name + seg, quote(value.substring(0, 60))); value = value.substring(60); seg++; } if (value.length() > 0) sb.addNV(name + seg, quote(value)); } else { sb.addNV(name, quote(value)); } } } return sb.toString(); }

对比两个版本可以发现,1.5.3版本之后多出了一段逻辑,在value.length() > 60 && splitLongParameters && encodeParameters 这个条件下会对文件名称做截断,导致文件名乱码,那么 splitLongParameters 和 encodeParameters 参数分别是由哪里控制的

private static final boolean encodeParameters = PropUtil.getBooleanSystemProperty("mail.mime.encodeparameters", true); private static final boolean splitLongParameters = PropUtil.getBooleanSystemProperty( "mail.mime.splitlongparameters", true);

看到这里,大家就明白,为什么设置 System.getProperties().setProperty(“mail.mime.splitlongparameters”, “false”); 可以解决中文乱码了,其实就是让文件名不要被截断,继续走以前的老逻辑。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3