Servlet常见乱码及解决办法

96
lkee6760
2017.03.01 22:55* 字数 1236

内容概要:

1.数据库乱码

2.控制台乱码

3.html网页乱码

4.下载文件文件名乱码


准备工作

1.查询"中国"的编码表:常见的中文编码表有GBKUTF-8

GBK编码:-42 -48 -71 -6 
UTF-8编码:-28 -72 -83 -27 -101 -67
UTF-8字节码用ISO-8859-1解码的结果:中国
GBK字节码用ISO-8859-1解码的结果:Öйú

2.建数据库

数据库结构

Field     Type          Null    Key     Default  Extra   
--------  ------------  ------  ------  -------  --------
uid       varchar(32)   NO      PRI     (NULL)           
username  varchar(100)  YES     UNI     (NULL)           
password  varchar(100)  YES             (NULL)           
email     varchar(100)  YES             (NULL)           
name      varchar(100)  YES             (NULL)           
sex       varchar(10)   YES             (NULL)           

3. 制作带<form action="/day15_test/RegisterServlet" method="post">表单的html网页,提交表单到Servlet中,Servlet获取表单信息写入数据库。


数据库乱码

  1. 现象:提交表单,使用QueryRunner向表格添加数据,中文内容乱码,显示西欧字符
QueryRunner qr = new QueryRunner(C3p0Utils.getDataSource());
String name = request.getParameter("name");
int row = qr.update("insert into user values(uid,username,password,email,name,sex)");

图片.png

显然乱码字符是由“中国”的UTF-8字节码用ISO-8859-1解码的结果。

  1. 分析
  2. 可能原因1:数据库内部编码设置错误,查询mysql的默认字符集,查看安装目录下的my.ini文件,ctrl+f查找default-character-set是否为utf8,如果不是改成utf8;经过查找,是utf8字符集,没有问题
  3. 可能原因2:请求对象使用的是UTF-8字符集编码,但是服务器使用ISO-8859-1解码,服务器与数据库之间通过字符流传递数据,所以出现西欧乱码,只需将请求对象添加编码信息,要求服务器使用UTF-8解码即可:
request.setCharacterEncoding("UTF-8");

显示结果

图片.png


控制台乱码

  1. 现象:控制台打印表单录入的信息出现乱码
String name = request.getParameter("name");
System.out.println(username + " " + password + " " + email + " " + name + " " + sex);

显示结果:

图片.png

  1. 原因分析
    请求对象使用的是UTF-8编码,而服务器用ISO-8859-1解码,结果中文不能正常显示
  2. 解决办法
  3. 分析1:从浏览器获取UTF-8编码的内容,传输到服务器后,通过ISO-8859-1解码西欧字符(但是内部字节码没有发生变化),再通过字符流传输到eclipse的控制台,eclipseUTF-8解码(个人习惯设置),自然都是问号了;
    这个流至始至终没有变的就是底层的字节码,所以只需用ISO-8859-1解码,在用UTF-8重新编码就可以了。
String name = request.getParameter("name");
name = new String(name.getBytes("ISO-8859-1"), "UTF-8");

图片.png

2.分析2:将浏览器的编码信息通过请求头传递给服务器,让服务器使用UTF-8编码

request.setCharacterEncoding("UTF-8");

图片.png

html网页乱码

  1. 现象: response.getWriter().write("<font color='green'>登录成功!</font>");输出在页面上的出现问号
    显示结果:
    图片.png
  2. 原因分析
    服务器默认编码集是ISO-8859-1,浏览器默认编码是GBK,传入浏览器后,浏览器解码错误,出现问号。
  3. 解决方法
  • 解决办法1:
    把响应对象用GBK编码,传到浏览器,浏览器用默认字符集解码
response.setCharacterEncoding("GBK");

显示结果:

图片.png

  • 解决办法2:
    将响应对象用UTF-8编码,并且通知浏览器使用UTF-8解码:
    如果只设置响应对象使用UTF-8编码(3个字节),使用GBK解码(2个字节)页面会多一些字符
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");

显示结果:

图片.png

解决办法3:
直接通知浏览器使用UTF-8字符集解码即可

response.setContentType("text/html;charset=UTF-8");

显示结果:

图片.png


下载文件中文乱码

  1. 现象:制作一个下载超链接<a href="/day15_test/DownLoadServlet?filename=你好.rar">你好.rar</a>下载文件,当有中文是,文件名不显示。
    DownLoadServlet源码
String filename = request.getParameter("filename");
filename = new String(filename.getBytes("ISO-8859-1"), "UTF-8");
//强制下载
response.setContentType(this.getServletContext().getMimeType("/resource/" + filename));
response.setHeader("Content-Disposition", "attachment; filename =" + filename);
//下载本质就是io流
InputStream is = this.getServletContext().getResourceAsStream("/resource/" + filename);
OutputStream os = response.getOutputStream();
byte[] b = new byte[1024 * 8];
int len = 0;
while((len = is.read(b)) != -1) {
    os.write(b, 0, len);
}
os.flush();is.close();os.close();
图片.png
  1. 原因分析
  • 通过String filename = request.getParameter("filename");获取文件全名,然后System.out.println(filename);在控制台上输出乱码,具体原因请参考本文第二条控制台乱码原因。
  • response.setContentType()获取文件的拓展名,文件拓展名都是西欧字符,不会产生乱码。
  • response.setHeader("Content-Disposition", "attachment; filename =" + filename);这条代码意思是将文件名传到浏览器,服务器默认将filename.getBytes("ISO-8859-1")解码发到服务器,而filenameUTF-8编码的,发送到服务器自然会乱码。
  • InputStream is = this.getServletContext().getResourceAsStream("/resource/" + filename);filename已经解码成UTF-8,而且没有外传,不会发生乱码现象。
  1. 解决办法
    办法1:
    filenameUTF-8解码,在用ISO-8859-1编码,服务器将filenameISO-8859-1解码,发送到浏览器,浏览器在使用UTF-8解码。
String filenameDownlaod = new String(filename.getBytes("UTF-8"), "ISO-8859-1");
InputStream is = this.getServletContext().getResourceAsStream("/resource/" + filenameDownlaod);

办法2:
上一个方法显然很绕,宗旨就是服务器传给浏览器能看懂的字节码,那么String filename = request.getParameter("filename");获取到的filename就是浏览器传过来的,自然能看懂了,在解决本地乱码问题时String newFilename = new String(filename.getBytes("ISO-8859-1"), "UTF-8");新定义一个文件名用于本地,发给浏览器用filename,自然就解决乱码问题了。完整代码如下

String filename = request.getParameter("filename");
String newFilename = new String(filename.getBytes("ISO-8859-1"), "UTF-8");
response.setContentType(this.getServletContext().getMimeType("/resource/" + newFilename));
response.setHeader("Content-Disposition", "attachment; filename =" + filename);
InputStream is = this.getServletContext().getResourceAsStream("/resource/" + newFilename);
OutputStream os = response.getOutputStream();
byte[] b = new byte[1024 * 8];
int len = 0;
while((len = is.read(b)) != -1) {
    os.write(b, 0, len);
}
os.flush();is.close();os.close();

显示结果

图片.png


小结

  1. 解决中文乱码主旨就是解码表和编码表一致;
  2. 本文只说明post方式乱码的情况,get方式没有讨论,其实可以使用控制台乱码的解决办法;
  3. 还遇到一个问题暂时没有解决,留待以后...
以下两个方法单独出现都能够正常显示跳转页面信息,但是同时出现时出现乱码现象:
response.getWriter().write(request.getMethod());
request.getRequestDispatcher("FailToRegist.html").forward(request, response);
而下面的两个方法同时出现不出现乱码,疑惑?字节流与字符流的关系
response.getOutputStream().write(request.getMethod().getBytes());
request.getRequestDispatcher("FailToRegist.html").forward(request, response)
  1. 参考Servlet 中文乱码问题及解决方案剖析(博客园-xiazdong)
  2. 其实乱码问题有一劳永逸的方法,暂不讨论。
java成长之路
Web note ad 1