Javamail处理unicode-1-1-utf-7编码的邮件

12/3/2006来源:其它邮件服务器软件人气:9139

客户总是报告某些email 在Outlook中可以正常显示,而在我用javamail编写的客户端却显示正文一片空白。看看日志报告的是 UnsupportedEncodingException 。我让客户把 email 原件从outlook 中下载下来发给我,打开一看,发现标题和正文使用的编码是 unicode-1-1-utf-7。

自从使用utf-8,很久没有遇到乱码的问题了。unicode-1-1-utf-7这个编码真是头一次看到。到google 上一搜,还真有不少人使用 javamail 遇到这个问题。不想这竟是 java 的 bug,参见 http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4304013 。难以理解为什么 java 一直到1.4.2还不支持UTF-7编码(j2sdk1.5 不知道搞定没有)。

看看RFC 和一些网上资料,基本了解了UTF-7,摘录一段如下:

UTF-7:A Mail-Safe Transformation Format of Unicode(RFC1642)。这是一种使用 7 位 ASCII 码对 Unicode 码进行转换的编码。它的设计目的仍然是为了在只能传递 7 为编码的邮件网关中传递信息。 UTF-7 对英语字母、数字和常见符号直接显示,而对其他符号用修正的 Base64 编码。符号 + 和 - 号控制编码过程的开始和暂停。所以乱码中如果夹有英文单词,并且相伴有 + 号和 - 号,这就有可能是 UTF-7 编码。

客户自然不能接受程序平台bug这样的借口,所以重要的是如何搞定这个编码。google 又是一阵狂搜,老实说没有google,我不知道我是否仍然可以继续程序员这份很有前途的工作。但话有说回来,google也让我产生了惰性,遇到不明白的例外的时候基本就不再自己思考了。网上提问题的人多,解决问题的人少,好不容易发现有人提供了解决的办法,不曾想竟是个日本人。

我是仇日的,除了三年前一个 Olympus 的数码相机,我一直严于律己,不染日货。但我还是对这个叫做 木下信 的日本人心生敬意。他写了整整一本书来共享其使用 javamail 的经验,《JavaMail完全解说》。其中一篇专门介绍了如何处理 UTF-7编码并提供源码下载。参见:http://www.sk-jp.com/cgi-bin/treebbs.cgi?kako=1%26amp;all=1220%26amp;s=1220,涉及的source可以从 http://www.sk-jp.com/software 下载。

结合日本人的解法,我的部分处理代码如下:

PRivate static String decodeStream(InputStream in, String charset) throws IOException {
//System.out.println(IOUtils.toString(in));
if (isUTF7(charset)) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
int c;

while ((c = in.read()) != -1) {
out.write(c);
}
byte[] bytes = out.toByteArray();

try {
ByteToCharUTF7 btc = new ByteToCharUTF7();
char[] chars = new char[bytes.length / 2 + 1];
btc.convert(bytes, 0, bytes.length, chars, 0, chars.length);
return new String(chars);
} catch (Exception e) {
log.warn("Error occurred while parse stream with charset " + charset
+ ". Cause by: " + e.getMessage());
//可能会抛出sun.io.ConversionBufferFullException, 那么直接用默认编码解码
return new String(bytes, "ISO8859-1");
}

} else {
return IOUtils.toString(in, charset);
}
}

private static boolean isUTF7(String charset) {
return "UTF-7".equalsIgnoreCase(charset) || "unicode-1-1-utf-7".equalsIgnoreCase(charset);
}

完全按照日本人的解法,时常会抛出sun.io.ConversionBufferFullException。问题可能出在它默认假定字符串一定包含有UTF7编码的内容,如果字符串只包含ASCII,那么就抛出了例外(UTF-7 对英语字母、数字和常见符号直接显示,而对其他符号用修正的 Base64 编码,见上UTF7的说明)。所以如果 catch 了例外,就直接用ISO8859-1编码处理。

希望java赶紧支持utf-7。我对unicode一直是一知半解,觉得编码这玩意挺复杂。主要是看到代码中遍布 >>、<< 这样的位操作符就心生怯意。到底不是科班出身啊。