前言
乱码问题在读写带有中文的文件和数据传输过程中是比较常见的,特别对于跟我一样的刚入行的初学者来说,更让人无比抓狂。因此本文将结合自己所遇到的问题和查找的资料,详细从乱码产生原因、解决方法记录总结servlet中文乱码问题。
一、乱码的根本原因
我们都知道,在计算机中任何文字、图片、视频等数据都是以“0”和“1”的形式在计算机中传递和储存的。也就说我们在传递数据前需要将我们想要传递按照一定的规则编码转换成统一的字符集,再转换成计算机可识别的“0”和“1”形式,而在读取数据时,需要将接收的数据按照一定的规则将接收的“0”和“1”形式的数据转换成我们能看得懂的字符。
根据上面的数据传递时的形式变换原理,我们可以理解为,数据的在计算机世界和人类世界形式转换的编码规则就相当于翻译员,当诉说方和倾听方采用的是同一个翻译员,那么交流就可以准确无误的进行。而如果诉说方和倾听方采用的翻译员不同,当一个翻译员不懂另一翻译员的语言规则时,就会出现翻译出错的问题。也就说,如果在传递数据时采用的编码规则与在接收数据时采用的解码规则不同的话,就会导致乱码,这也是乱码的根本原因。
二、Response响应乱码具体原因和解决方法
1.采用字节流响应
- 问题:在servlet中运行这段代码,web客户端出现乱码。
ServletOutputStream outputStream = response.getOutputStream(); outputStream.write("下载中.....".getBytes());
- 具体原因
servlet输出数据时采用的字符集是“UTF-8”,而浏览器接收到数据解析时采用的字符集“ISO-8859-1”,编码和解码的规则不同,从而导致乱码。这种乱码在不同的游览器有不同的效果,如果该浏览器与编码时用的字符集一样,则不会出现乱码。 - 解决方法
上次在《HTTP协议抓包分析》中说过,请求头和响应头中的Content-Type头可以告诉浏览器用什么字符集对接收的数据进行解码。因此我们可用下面的代码将浏览器打开时采用的字符集和发送时将中文转成字节数采用的字符集设置成一样便可以解决。具体代码如下://使用字节流的方式输出中文 ServltOutputStream outputStream = responser.getOutputStream(); //设置浏览器默认打开的时候采用的字符集 response.setHeader("Content-Type","text/html;charset=UTF-8"); //设置中文转换成字节数组字符集编码 outputStream.write("下载中....".getBytes("UTF-8"));
2.采用字符流响应
- 问题:通过servlet的response响应向web客户端回写中文时,web客户端出现乱码。
response.getWriter().println("下载中....")
- 具体原因
当采用字符流读写数据是先进入缓冲区,在向外发送的。而response采用的默认缓冲区编码是ISO-8859-1,这个字符集不支持中文的,因此当中文数据字符流一旦进入到response缓存区,便会发生了乱码。这种乱码在任何浏览器都会发生,且后面不管如何设置浏览器解码采用的字符集都无法正确解析。 - 解决方法
这类乱码主要出现的主要问题就是response缓冲区的默认编码字符集不识别中文,所以我们把response获得字符流缓冲区的编码设置成能识别中文的字符集,如UTF-8,并且将浏览器打开的时候默认采用的字符集设置成与response缓冲区的采用的字符集一致即可解决。具体有下面两种代码解决方式://方式一 //设置response获得字符流的缓存区的编码 response.setCharacterEncoding("UTF-8"); //设置浏览器默认打开的时候采用的字符集 response.setHeader("Content-Type","text/html;charset=UTF-8"); //回写 response.getWriter().println("下载中...."); //方式二 //直接利用setContentType()方法便可以同时设置缓存区和浏览器默认字符集 response.setContentType(text/html;charset=UF-8); //回写 response.getWriter().println("下载中....");
三、Request请求乱码具体原因和解决方法
1.Post请求
- 问题:当web客户端采用Post方式向servlet服务器提交表单后,运行下面这段代码,在服务器端打印表单的值时乱码。
String name = request.getParameter("name"); System.out.println("用户名:" + name);
- 具体原因
与response以字符流的方式响应一样,客户端用post方式向服务器提交表单,request接收到提交的表单后会先放入缓冲区,而缓存区的默认编码字符集是ISO-8859-1,而该字符集是不支持中文的,因此接收的数据一旦进入缓存区就发生了乱码,从而导致输出时乱码。 - 解决方法
这类乱码主要出现的主要问题就是request缓冲区的默认编码字符集不识别中文,所以我们把request缓冲区的编码设置成能识别中文的字符集,如UTF-8。具体代码如下://设置request缓冲区的编码为UTF-8 request.setCharacterEncoding("UTF-8"); //输出 String name = request.getParameter("name"); System.out.println("用户名:" + name);
2.Get请求
- 问题:当web客户端采用Get方式向servlet服务器提交表单后,运行下面这段代码,在服务器端打印表单的值时乱码。
String name = request.getParameter("name"); System.out.println("用户名:" + name);
- 具体原因
当web客户端采用Get方式向servlet服务器提交数据时,数据会加在请求行的后面,并且在地址栏中会进行一次URL的编码,被接受后进入request缓存区,再采用ISO-8859-1字符集进行一次编码,所以如果再从request缓存区中输出后没有采用ISO-8859字符集解码恢复UTF-8的编码,在打印输出时就会乱码。 - 解决方法
方法一:将存入request缓冲区的数据以ISO-8859字符集解码获取,然后再通过UTF-8字符集解码获得原始的数据,具体代码如下://获取数据 String name = request.getParameter("name"); //利用String类的构造方法转换 String userName = new String(name.getBytes("ISO-8859"),"UTF-8"); //输出 System.out.println("用户名:" + userName);
方法二:在tomcat的server.xml中,设置元素的属性URIEncoding="UTF-8",useBodyEncodingForURI=“true”,即指定服务器对get和post统一按照UTF-8解码,要求tomcat管理下的所有web应用都要使用utf-8编码,并且请求体和URI使用相同的编码格式。具体代码如下:<Connector connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443" URIEncoding="UTF-8" useBodyEncodingForURI="true"/>