Java Mail 附件名太长导致接收端附件名解析出错

http://blog.csdn.net/fl_zxf/article/details/60126910

http://blog.csdn.net/wty19/article/details/50607411


0x00(测试条件)

附件名:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.txt


0x01(现象)

新浪邮箱解析出错:

QQ邮箱解析出错:


0x02(分析问题)

本地没问题,到 Linux 环境才出错。所以抓了本地的包和 Linux 下的包比较。

本地的包(之前用比较长的中文名测试抓的包):

------=_Part_18_1324418920.1488440125843

Content-Type: application/octet-stream;

name="=?UTF-8?Q?=E4=B8=AD=E6=96=87=E5=AD=97=E7=AC=A6201711=2Edocx?="

Content-Transfer-Encoding: base64

Content-Disposition: attachment;

filename="=?UTF-8?Q?=E4=B8=AD=E6=96=87=E5=AD=97=E7=AC=A6201711=2Edocx?="

Linux 的包:

------=_Part_0_528597028.1488450122516

Content-Type: text/plain; charset=us-ascii;

name*0=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa;

name*1=a.txt

Content-Transfer-Encoding: 7bit

Content-Disposition: attachment;

filename*0=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa;

filename*1=a.txt

可以看到问题是文件名太长被拆分了


0x03(查找根源)

看 Java 代码

String encodeName = MimeUtility.encodeWord(name);
UrlResource inputStreamSource = new UrlResource(attach.getUrl());
helper.addAttachment(encodeName, inputStreamSource); // 自己的业务代码
addAttachment(attachmentFilename, inputStreamSource, contentType);	// 跟进去
addAttachment(attachmentFilename, dataSource);	// 跟进去

mimeBodyPart.setFileName(MimeUtility.encodeText(attachmentFilename)); // 跟进去
setFileName(this, filename);	// 跟进去
part.setHeader("Content-Disposition", cd.toString()); // 跟进去
sb.append(list.toString(sb.length() + 21));	// 跟进去

if (value.length() > 60 &&
      splitLongParameters && encodeParameters) {
    int seg = 0;
    name += "*"; // 省略...

已经看出是怎么回事了

 

0x04(解决)

 Main方法下加

System.setProperty("mail.mime.splitlongparameters", "false"); // linux 会默认为 true,会截断附件名

0x05PS

这是 RFC2231 的规定,估计国内的还不支持(个人猜想)

 

0x06(走过的弯路)

之前没头绪。以为是 Linux 的限制;也怀疑过“MimeUtility.encodeWord(name)”的问题。

跟踪源码的时候看错了 jar 包,应该是 com.sun.mail:javax.mail:1.5.6,还不是 javax.mail:mail:1.4.5



------------  记一个JavaMail 附件乱码的问题


     说到乱码,很多人都遇到过,“哎呀,你这个编码是不是UTF-8!”,“你这个会不会操作系统不一致导致的?”,“肯定是两边编码不一致”。不过我们今天说的问题,还真不是这个问题导致的。

问题
   用JavaMail 发邮件,带上附件,闪电邮客户端收到后,附件名有时乱码,有时非乱码。查看Java端代码:
                 String name = MimeUtility.encodeText(name, null) + ".xlsx";
		messageBodyPart.setFileName(name);
貌似已经做过编码转换了。
令人感到奇怪的是,稍微改变附件名,乱码就消失了。

发现
   收集各种信息的时候,突然发现 Ubuntu的小伙伴 在 ThunderBird下收邮件表示没有异常,Mac党表示自带的邮件工具收件也没有问题。那是不是操作系统字符编码问题呢? 可是遗憾的是,在网页版上,依旧是乱码。 这时候初步怀疑是邮件系统不兼容的问题了,来看邮件源码:

Content-Type: application/octet-stream; 
	name*0="=?utf-8?B?5rWL6K+V5qCH6aKYLS0tMDAx5oiR6KaB5LiK?==?utf-8?B?5"; 
	name*1="a2mQUJDREXvvIzlkKzor7TopoHotrPlpJ/plb8=?=.xlsx"
Content-Transfer-Encoding: base64
Content-Disposition: attachment; 
	filename*0="?utf-8?B?5rWL6K+V5qCH6aKYLS0tMDAx5oiR6KaB5LiK?==?utf-8?B?5"; 
	filename*1="a2mQUJDREXvvIzlkKzor7TopoHotrPlpJ/plb8=?=.xlsx"
这串就是 有些系统乱码有些系统 正常显示的 邮件源码。。
对比在邮件客户端上的非乱码邮件:

Content-Type: application/octet-stream; name="=?utf-8?B?5rWL6K+V?=.xlsx"
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="=?utf-8?B?5rWL6K+V?=.xlsx"
 filename*0,filename*1 和 filename 的区别,猜测也许就是 邮件客户端不支持这种filename*0,filename*1 协议导致的问题。

分析
  有了以上的想法,就开始来看源码。
   MimeBodyPart 这个类中的 setFileName 方法 用到一个 ParameterList 在ParameterList 的 toString 类中找到下面一段:

   if (v instanceof MultiValue) {
		// ....
			ns = name + i + "*";
		//...
		}
	    } else if (v instanceof Value) {
		/// ...
	    } else {
		
		if (value.length() > 60 &&
				splitLongParameters && encodeParameters) {
		    int seg = 0;
		    name += "*";
		    /// ....
		}
这个类在邮件附件属于 MultiValue 会把 名字用name + i 隔开 ,在名字大于 60个字符的时候也会主动截断,这也就是 javamail 中 附件的命名规则, 名字太长会被截断~~!
中文在base64 加密后,超过60个字符那是妥妥的有可能。这种截断文件名的模式在某些客户端,并不能很好的支持。

    
解决
  解决就很容易了, 代码里有 splitLongParameters 这个参数, 观察了下 对应于一个环境变量,如果想不截断文件名,只要在程序运行之初加上:
 System.setProperty("mail.mime.splitlongparameters","false");
  就可以了。测试,解决。。。
  另外由于发现了这个问题,google到了 java mail 的完整配置,
    JAVA Mail System 环境变量  包括是否自动编码等配置,曾经没怎么关注过。


总结
   邮件系统的不兼容是导致这个错误的根本原因,还真不是编码问题,所以有的时候看问题还不能那么想当然。这个测试未必能测出,毕竟要满足名字足够长这个条件。在这里分享这个问题,以免JAVA党同学重复踩坑



  • 7
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值