写在前面
今天,再做微信登录功能时,出现了java.sql.SQLException: Incorrect string value: ‘\xF0\x9F\x92\x94’ for column ‘nick_name’,
后来仔细排查,发现我的微信昵称后面带有一个表情(占四个字节)
而mysql的utf8编码的一个字符最多3个字节,但是一个emoji表情为4个字节,所以utf8不支持存储emoji表情,导致报错
解决方案
-
服务器端转换数据库存储编码
数据库目前采用的编码为UTF8, 转换为可支持4个字节的utf8mb4_unicode编码。
如图所示:
利: 客户端存储和请求数据的时候不需要都去做转换
弊:1)转换编码格式可能会导致现有数据库的数据发生乱码;
2)转换编码格式之后,可能对后续的全文搜索功能有影响,大多数中文搜索引擎支持的编码格式为UTF-8;
3)若Android端的编码与数据库转换后的编码不符,将对Android端产生同样的问题。
-
客户端转换编码
客户端输入内容时候,统一存储为服务端数据库支持的编码;客户端请求内容的时候,需要根据客户端支持的编码对请求到的数据进行相应的转换
工具类代码如下所示:
public class EmojiConverterUtil {
/**
* @Description 将字符串中的emoji表情转换成可以在utf-8字符集数据库中保存的格式(表情占4个字节,需要utf8mb4字符集)
* @param str
* 待转换字符串
* @return 转换后字符串
* @throws UnsupportedEncodingException
* exception
*/
public static String emojiConvert1(String str)
{
String patternString = "([\\x{10000}-\\x{10ffff}\ud800-\udfff])";
Pattern pattern = Pattern.compile(patternString);
Matcher matcher = pattern.matcher(str);
StringBuffer sb = new StringBuffer();
while(matcher.find()) {
try {
matcher.appendReplacement(
sb,
"[["
+ URLEncoder.encode(matcher.group(1),
"UTF-8") + "]]");
} catch(UnsupportedEncodingException e) {
}
}
matcher.appendTail(sb);
return sb.toString();
}
/**
* @Description 还原utf8数据库中保存的含转换后emoji表情的字符串
* @param str
* 转换后的字符串
* @return 转换前的字符串
* exception
*/
public static String emojiRecovery2(String str){
String patternString = "\\[\\[(.*?)\\]\\]";
Pattern pattern = Pattern.compile(patternString);
Matcher matcher = pattern.matcher(str);
StringBuffer sb = new StringBuffer();
while(matcher.find()) {
try {
matcher.appendReplacement(sb,
URLDecoder.decode(matcher.group(1), "UTF-8"));
} catch(UnsupportedEncodingException e) {
}
}
matcher.appendTail(sb);
return sb.toString();
}
}
利:减少服务器端转换编码带来的风险和压力
弊:客户端需要根据不同的设备系统自行做编码转换
建议
如果服务器时自己的话,我觉得采取第一种方案挺方便的,也不需要编码什么的,转来转去。
如果数据时存储在他人服务器上的(比如阿里云),但是别人的服务器不允许存储emoji,那么我觉得采取第二种方案会好很多.