关于JavaMail发送邮件的技术分享

关于JavaMail发送邮件的技术分享

代码中发送邮件的需求我们可能会经常遇到,这里记录并分享一些在发送邮件时遇到的问题;

JavaMail附件乱码问题

在发送携带附件的邮件时,部分类型邮箱收到的附件名会出现乱码问题如:
在这里插入图片描述
部分类型邮箱(如163邮箱)收到的附件名又会变成“AT XXX”如:
在这里插入图片描述
甚至有些附件收到之后连后缀名都变了;

追踪源码

接下来我们来看一下JavaMail发送邮件的源码;既然是附件名称的问题我们就先点进添加附件的方法看一下:

    public void addAttachment(String attachmentFilename, DataSource dataSource) throws MessagingException {
        Assert.notNull(attachmentFilename, "Attachment filename must not be null");
        Assert.notNull(dataSource, "DataSource must not be null");

        try {
            MimeBodyPart mimeBodyPart = new MimeBodyPart();
            mimeBodyPart.setDisposition("attachment");
            mimeBodyPart.setFileName(MimeUtility.encodeText(attachmentFilename));
            mimeBodyPart.setDataHandler(new DataHandler(dataSource));
            this.getRootMimeMultipart().addBodyPart(mimeBodyPart);
        } catch (UnsupportedEncodingException var4) {
            throw new MessagingException("Failed to encode attachment filename", var4);
        }
    }

可以看到,这里对文件名称做了两件事:

  1. 对文件名称进行转码;

  2. 将文件名称赋值给MimeBodyPart对象;

所以当我们点进mimeBodyPart.setFileName()方法时拿到的name已经是转码之后的值:
在这里插入图片描述
此时它的长度已经是72,这将影响之后的操作;而我们原始的文件名长度只有28;

static void setFileName(MimePart part, String name) throws MessagingException {
        //前略
        ...
        ContentDisposition cd = new ContentDisposition(s == null ? "attachment" : s);
        String charset = MimeUtility.getDefaultMIMECharset();
        ParameterList p = cd.getParameterList();
        if (p == null) {
            p = new ParameterList();
            cd.setParameterList(p);
        }

        if (encodeFileName) {
            p.setLiteral("filename", name);
        } else {
            p.set("filename", name, charset);
        }
        part.setHeader("Content-Disposition", cd.toString());
        //后略
        ...

    }

这里我们可以看到,它是将文件名设置给了ParameterList这个对象,而在其内部有一个map;这里的set其底层实际上是将其put进入了这个map:
在这里插入图片描述
到这里我们就知道它将文件名称存入哪里了;

它接下来调用了part.setHeader("Content-Disposition", cd.toString());将整个ContentDisposition对象toString并放入了part的header当中;问题就出现在这里;ContentDisposition对象重写了toString方法,我们点进cd.toString():

 //class ContentDisposition
 public String toString() {
        if (this.disposition == null) {
            return "";
        } else if (this.list == null) {
            return this.disposition;
        } else {
            StringBuilder sb = new StringBuilder(this.disposition);
            sb.append(this.list.toString(sb.length() + 21));
            return sb.toString();
        }
    }

在这里插入图片描述

可以看到它这里是调用了this.list.toString(int used)方法,而它的list属性正是ParameterList对象;点进这个toString方法可以看到这样一段代码:
在这里插入图片描述
这里的意思就是如果value的长度大于60并且splitLongParametersencodeParameters

都为true的时候,则会将value分割成多个长度不超过60的字符;而splitLongParameters

encodeParameters的值如下:
在这里插入图片描述

可以看到他们的默认值都是true;所以我们在不进行设置的情况下,转码之后的名称很可能会超过60,则就会被分割;而part的header中存的值就是分割之后的值:
在这里插入图片描述
由于不同的邮件服务商对attachment解析的兼容度不一致,有些邮箱可以兼容这种写法,而有些邮箱则不兼容,就会导致部分邮箱在识别名文件名称时出现各种各样的错误,像163邮箱就不兼容,但是他们如果解析不了,则会使用自己生成的附件名去代替;就像我们上面见到的"ATXXX"等。

解决办法

我们上面看到它在分割文件名称时会判断两个值是否为true,即splitLongParametersencodeParameters :

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

可以看到这两个值是通过PropUtil.getBooleanSystemProperty()方法取值,并且都传了一个默认值;

PropUtil.getBooleanSystemProperty()方法则是从System.getProperties()中获取,如果获取不到则会返回默认值:

public static boolean getBooleanSystemProperty(String name, boolean def) {
        try {
            return getBoolean(getProp(System.getProperties(), name), def);
        } catch (SecurityException var4) {
            try {
                String value = System.getProperty(name);
                if (value == null) {
                    return def;
                } else if (def) {
                    return !value.equalsIgnoreCase("false");
                } else {
                    return value.equalsIgnoreCase("true");
                }
            } catch (SecurityException var3) {
                return def;
            }
        }
    }

所以我们可以通过下列代码来阻止它对文件名进行分割;

System.getProperties().setProperty("mail.mime.splitlongparameters", "false");
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值