解决javaMail发送QQ邮件,附件名乱码的问题

  • 问题背景
    • 具体问题
  • 解决思路
    • 源码Debug
  • 解决方式

问题背景

项目里面使用javaMail发QQ邮件,日志显示我们这边传过去的附件名是正常的,个别名字会出现乱码。
客户不断反馈,悟空面对这个问题在搞了两天无果后,只能求救于部门老大。老大看了看我可怜巴巴的眼神,拍着胸脯保证交给他了,结果在他debug过无数层源码,打开无数个百度页后,告诉不好解决,先延期吧。
悟空没有办法,只能拿过来自己再一点一滴debug看下,期望能解出来这个问题。

具体问题

首先给大家看下我们的问题,这个是乱码的邮件压缩包

正常的应该是这样的:

解决思路

  1. 观察正常得字母和汉字的压缩包不会有问题,有问题的是这种带有特殊符号的压缩包。有同学说直接禁止这样命名不就好了嘛,客户是上帝呀,客户要这样命名那也没有办法。所以解决问题才是正解。
  2. degbug查看发送压缩包前,传入的压缩包名字是正确的,考虑是QQ邮箱有问题,或者是名字的编码有问题,将发送方法改为发送到163邮箱,发现也有该问题。排除QQ邮箱的问题,肯定是压缩包处理时对名字的处理有问题。二话不说,悟空马上考试看源码

源码Debug

debug源码查看javaMail附件的处理方法MimeMessageHelper.addAttachment ,一路点到附件名的处理方法MimeUtility.encodeWord方法

private static String encodeWord(String string, String charset,String encoding, boolean encodingWord)throws UnsupportedEncodingException {// If 'string' contains only US-ASCII characters, just// return it.int ascii = checkAscii(string);if (ascii == ALL_ASCII)return string;// Else, apply the specified charset conversion.String jcharset;if (charset == null) { // use default charsetjcharset = getDefaultJavaCharset(); // the java charsetcharset = getDefaultMIMECharset(); // the MIME equivalent} else // MIME charset -> java charsetjcharset = javaCharset(charset);// If no transfer-encoding is specified, figure one out.if (encoding == null) {if (ascii != MOSTLY_NONASCII)encoding = "Q";elseencoding = "B";}boolean b64;if (encoding.equalsIgnoreCase("B")) b64 = true;else if (encoding.equalsIgnoreCase("Q"))b64 = false;elsethrow new UnsupportedEncodingException("Unknown transfer encoding: " + encoding);StringBuilder outb = new StringBuilder(); // the output bufferdoEncode(string, b64, jcharset, // As per RFC 2047, size of an encoded string should not// exceed 75 bytes.// 7 = size of "=?", '?', 'B'/'Q', '?', "?="75 - 7 - charset.length(), // the available space"=?" + charset + "?" + encoding + "?", // prefixtrue, encodingWord, outb);return outb.toString();}

上面的代码我们可以看出,javaMail在对名字处理之前,首先对传入的名字进行了编码的判断,int ascii = checkAscii(string);encoding == null的情况下,对将要对名字进行的编码指定了encoding方法,如果encoding.equalsIgnoreCase("B")则进行Base64编码,发送给QQ邮箱。
此刻我们没有指定encoding,debug进checkAscii(string);看下,会默认什么编码

 static int checkAscii(String s) {int ascii = 0, non_ascii = 0;int l = s.length();for (int i = 0; i < l; i++) {if (nonascii((int)s.charAt(i))) // non-asciinon_ascii++;elseascii++;}if (non_ascii == 0)return ALL_ASCII;if (ascii > non_ascii)return MOSTLY_ASCII;return MOSTLY_NONASCII;}

在上述方法中nonascii((int)s.charAt(i))对US-ASCII进行校验,如果全是US-ASCII码,则返回ALL_ASCII==1,如果超过一半是 US-ASCII码,则返回MOSTLY_ASCII==2,否则就返回MOSTLY_NONASCII==3
代码块一中我们可以看出只要不是MOSTLY_NONASCII==3,就会使用Q编码即非Base64编码,MOSTLY_NONASCII==3时则使用Base64编码。
此时我们再看名字"行业媒体稿件${random}.zip ",这个名字有一半以上都是可以ASCII码,所以使用了Q即非Base64编码。

doEncode(string, b64, jcharset, // As per RFC 2047, size of an encoded string should not// exceed 75 bytes.// 7 = size of "=?", '?', 'B'/'Q', '?', "?="75 - 7 - charset.length(), // the available space"=?" + charset + "?" + encoding + "?", // prefixtrue, encodingWord, outb);

通过doEncode方法我们可以看出名字如果超过75个字符,我们会截断成两端拼接,我们的名字长度不超过75字符,不是字符长度引起的问题,继续排查。进入doEncode 代码,我们可以看到

 ByteArrayOutputStream os = new ByteArrayOutputStream();OutputStream eos; // the encoderif (b64) // "B" encodingeos = new BEncoderStream(os);else // "Q" encodingeos = new QEncoderStream(os, encodingWord);

QEncoderStream方法对名字数据流进行了编码处理,问题点一定再编码这块,继续看源码

 public QEncoderStream(OutputStream out, boolean encodingWord) {super(out, Integer.MAX_VALUE); // MAX_VALUE is 2^31, should// suffice (!) to indicate that// CRLFs should not be inserted// when encoding rfc822 headers// a RFC822 "word" token has more restrictions than a// RFC822 "text" token.specials = encodingWord ? WORD_SPECIALS : TEXT_SPECIALS;}

可以看到定义了全局变量specials 对流的字符集进行判断,继续追踪,看该变量什么时候调用

 @Overridepublic void write(int c) throws IOException {c = c & 0xff; // Turn off the MSB.if (c == ' ')output('_', false);else if (c < 040 || c >= 0177 || specials.indexOf(c) >= 0)// Encoding required. output(c, true);else // No encoding requiredoutput(c, false);}

这个方法将编码后的名字流进行输出,此时我们可以看到write(int c)对每个字节进行处理,output()方法里进行流拼接,很明显问题出在拼接的流之间字符集冲突乱码啦。
至此问题点发现,是由于名字时含有非ASCII造成的,所以干脆,在封装压缩包的之后,直接指定名字的编码方式即可,统一使用Base64编码。

解决方式

从上面问题发现,只需要指定压缩包名的编码方式即可,Debug源码

    public static String encodeText(String text, String charset,String encoding)throws UnsupportedEncodingException {return encodeWord(text, charset, encoding, false);}

发现encodeWord(text, charset, encoding, false);方法可以指定名字编码方式,所以完整的发送方法为

public void sendAttachmentsMail(MailSendBo mailSendBo)throws MailSendException, MessagingException, UnsupportedEncodingException {JavaMailSenderImpl javaMailSender = initJavaMailSender(mailSendBo.getFromEmail(), mailSendBo.getAuthCode());MimeMessage message = javaMailSender.createMimeMessage();MimeMessageHelper helper = new MimeMessageHelper(message, true);helper.setFrom(mailSendBo.getFromEmail());helper.setTo(mailSendBo.getToEmail());helper.setSubject(mailSendBo.getSubject());helper.setText(mailSendBo.getContent());List<String> filePathList = mailSendBo.getFilePathList();if (CollectionUtils.isNotEmpty(filePathList)) {for (String url : filePathList) {File tempFile = new File(url);//指定压缩包名的编码方式和字符集String fileName = MimeUtility.encodeText(tempFile.getName(), "UTF-8", "B");helper.addAttachment(fileName, tempFile);}}javaMailSender.send(message);}

下面展示一下效果:

解决问题,老大都没解决被悟空解决啦,可是怎么才能不高调的炫耀一下呢,那么就写篇文章吧

每日一语

最可怕的是想太多,做太少,而我们恰恰处于这个年纪,停下来,先走两步,也许不一定是想的那样

点关注,不迷路,更多精彩关注微信公众号

QQ邮箱炸啦,我的附件名怎么乱码?解决javaMail发送QQ邮件,附件名乱码的问题相关推荐

  1. JavaMail发送QQ邮件

    JavaMail发送QQ邮件 1.开启QQ服务并获得授权码 授权码是QQ邮箱推出的,用于登录第三方客户端的专用密码 适用于登录以下服务:POP3/IMAP/SMTP/Exchange/CardDAV/ ...

  2. get、post请求参数乱码解决方法(qq:1324981084)

    get.post请求参数乱码解决方法(qq:1324981084) 参考文章: (1)get.post请求参数乱码解决方法(qq:1324981084) (2)https://www.cnblogs. ...

  3. java 发送邮件昵称_利用JavaMail发送QQ邮件

    一.RFC882文档简单说明 RFC882文档规定了如何编写一封简单的邮件(纯文本邮件),一封简单的邮件包含邮件头和邮件体两个部分,邮件头和邮件体之间使用空行分隔. 邮件头包含的内容有: from字段 ...

  4. 【Java】JavaMail发送QQ邮件邮件

    荐读 JavaMail邮件发送不成功的那些坑人情况及分析说明 javax.mail.AuthenticationFailedException异常的处理 QQ邮箱如何开启POP3/SMTP服务 发送说 ...

  5. outlook邮箱发送邮件时出错,报告错误(0x800ccc78)“无法发送此邮件。请在账户属性中验证电子邮件地址”,解决方法...

    转载于:https://my.oschina.net/dapsjj/blog/804262

  6. 解决smtp发送中文名称附件乱码或者失败的问题

    核心代码: my_att = MIMEText(open(file_full_path, 'rb').read(), 'base64', 'utf-8') my_att['Content-Type'] ...

  7. java qq邮箱发送端口号_java实现qq邮箱的发送

    准备工作 开启PO3/SMTP服务 打开qq邮箱>账户 记住这串授权码 会用到 java发送qq邮箱实现步骤 创建一个javase的工程 3. 编写核心代码 我这里封装了 也可以不封装 /** ...

  8. java mail 554_javaMail 163 邮箱发 qq 邮箱 总是报 554 DT:SPM 163 smtp11

    163 邮箱发 qq 邮箱 总是报 554 DT:SPM 163 smtp11 554 DT:SPM 发送的邮件内容包含了未被许可的信息,或被系统识别为垃圾邮件.请检查是否有用户发送病毒或者垃圾邮件: ...

  9. 2010 outlook导入QQ邮箱联系人

    今天打开foxmail的时候,发现自己的已经是老版本了,为了体验一下最新的版本,从网上下载安装了最新版本7.0.1.90.可是当我在添加新帐号的时候出现"未响应"并且自己就关闭了, ...

最新文章

  1. 感知和行动的贝叶斯模型
  2. DataGrid中的高级ToolTip
  3. Hadoop框架:MapReduce基本原理和入门案例
  4. myeclipse9 maven web 环境
  5. Strut2页面传参跳转 --Struts2
  6. 我有一箱01年的茅台酒现在值多少钱
  7. 使用高速通道加速iOS版本审核
  8. 集合竞价如何买入_世界上最稳健的抓涨停方法“10分钟集合竞价”选股诀窍,买入直接稳赚10个点,赚到笑...
  9. TDirectory.GetParent获取指定目录的父目录
  10. Operation和OperationQueue实战:异步下载图片并给图片加滤镜
  11. 如何完全卸载mysql呢
  12. Eclipse 反编译插件下载地址
  13. 机械制图计算机识图,机械制图基础知识
  14. 计算机成绩统计优秀率,高校学生考试成绩的数据分析模式与可视化研究
  15. 2.心理学家-威廉.詹姆斯
  16. Squid安装及运行控制_wuli大世界_新浪博客
  17. 用python祝福父亲节_python 计算 父亲节
  18. AutoCAD WS for iPhone, iPod toch, and iPad
  19. 教育培训机构如何打赢“教育营销流量战“?
  20. Speedoffice(word)如何生成目录

热门文章

  1. Codesys电子凸轮功能的设计与可视化仿真
  2. 设置日语输入法遇到的各种问题
  3. matlab 分水岭法,分水岭算法Matlab实现——三种方法 | 学步园
  4. 移植tslib,测试电容屏
  5. 分享材料(不断更新)
  6. HTML 标签 (HTML超文本标记语言)
  7. 微商城搭建教程,手把手教你开通自己的线上/定制微商城!
  8. 推荐一款特别厉害的在线工具,程序员的百宝箱
  9. CSS重叠解决边框相邻变粗问题
  10. win10自带的框选截图快捷键