A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

本帖最后由 大山哥哥 于 2018-3-30 19:15 编辑

        在JavaEE开发过程中,由于使用中文字符串的原因,经常会出现乱码,而乱码问题的解决也着实让很多同学焦头烂额。今天我们就来讨论一下乱码问题的根源,并分析一下常见的几种乱码的解决方案。        乱码问题一:下载时的中文文件名乱码
        文件下载时,如果提供的资源是一个中文文件名的,如:“图片.jpg”,就会出现下载完毕后文件名乱码的情况,这是为什么呢?首先我们分析一下文件下载的原理。文件下载需要我们后台提供两个头和一个流:Content-Type,Content-Disposition和文件输入流。这里的问题就出现在Content-Disposition这个头上,Content-Disposition头中包含filename参数,也就是文件名,而在HTTP的请求头里是不允许出现中文字符的(原因就是HTTP协议是老外制定的,并没有考虑中文的问题),所以在指定文件名的时候,直接就出现了中文乱码,因为传输文件名的时候就不允许用中文。那怎么解决呢?既然非中文字符不允许出现在请求头上,那我们就把中文文件名转成非中文字符再传输,但新的问题又出现了:我把中文转成非中文,那浏览器接收到数据之后怎么知道我传过去的非中文对应的是哪个中文字符呢?所以我们需要和浏览器约定一个大家都认可的转码方式,把中文转成非中文字符传输,到浏览器那端再把非中文转回中文显示。在这里说一下,火狐浏览器使用的转码方式是Base64,而其他家的浏览器使用的是UrlEncoder编码。所以大家在处理的时候只要按照下面的方式对中文转码即可:
[AppleScript] 纯文本查看 复制代码
// 根据浏览器的类型处理中文文件的乱码问题:
                String agent = request.getHeader("User-Agent");
                System.out.println(agent);
                if(agent.contains("Firefox")){
                        filename = base64EncodeFileName(filename);
                }else{
                        filename = URLEncoder.encode(filename,"UTF-8");
                }
                
                // 2.2设置Content-Disposition头 attachment附件,指定当前文件以下载的形式打开
                response.setHeader("Content-Disposition", "attachment;filename="+filename);
......

public static String base64EncodeFileName(String fileName) {
                BASE64Encoder base64Encoder = new BASE64Encoder();
                try {
                        return "=?UTF-8?B?"
                                        + new String(base64Encoder.encode(fileName
                                                        .getBytes("UTF-8"))) + "?=";
                } catch (UnsupportedEncodingException e) {
                        e.printStackTrace();
                        throw new RuntimeException(e);
                }
        }

        乱码问题二:response乱码
        响应数据的时候使用response对象输出到浏览器,这时候可能会传输中文字符串,也会有乱码的情况。那么response的乱码又是怎么导致的呢?
        respones 的乱码分成两种,一种是使用response的getWriter方法获取字符输出流输出,一种是使用getOutputStream获取字节输出流输出,这两种输出方式乱码的原因不同,所以我们分开说。首先是字符流输出,字符流输出的时候使用了Writer,而Writer的print方法接受到中文之后是会先把字符串缓存入缓冲区的。如下图
        Writer的缓冲区有默认的编码,和response 的编码保持一致,而response对象由Tomcat服务器创建,所以和Tomcat的默认编码保持一致,在Tomcat8以前的版本,默认的编码是ISO-8859-1,所以Writer缓冲区的编码就是ISO-8859-1,而这是一个纯西欧字符的字符集,所以不认识中文,导致中文字符串从Writer的print接收的时候就已经是乱码了,和图中标注的一样,缓冲区中都是乱码,那浏览器接受到的怎么处理都是乱码。解决方式很简单,把Writer的编码改掉,也就是调用response.setCharacterEncoding(“UTF-8”)。但是这只设置了服务器端输出的编码,浏览器获取到数据之后也不知道这个数据是用的什么字符集,所以还得指定一下浏览器打开数据的时候的字符集,也就是调用resposne.setHeader(“Content-Type”,”text/html;charset=UTF-8”);指定浏览器打开数据的字符集,这样,服务端和客户端的字符集保持一致,而且都支持中文,也就把乱码解决了。
        再来说字节流乱码问题,也是一样,字符串想要用字节流传输,必须调用字符串的getBytes方法转成字节数组,然后用字节流传输。而getBytes不指定字符集调用的话,会使用workspace的默认字符集,所以可能会乱码也可能不会。为了保证字符集的统一性,我们需要手动指定getBytes转码时的字符集,也就是调用“中文”.getBytes(“UTF-8”);。而数据传输到浏览器还是不知道打开的字符集是什么,所以还是继续调用resposne.setHeader(“Content-Type”,”text/html;charset=UTF-8”);指定浏览器打开数据的字符集。这样中文字符串就能以支持的字符集转成字节数组传输,再由浏览器用相同的字符集解析显示了。这两个API可以由一个API替代:response.setContentType(“text/html;charset=UTF-8”);,因为此API内部已经封装好了这两步处理。

        总结一下,response乱码的解决方式如下:
        字节流:
                设置浏览器默认打开的编码:
                        resposne.setHeader(“Content-Type”,”text/html;charset=UTF-8”);
                设置中文字节取出的时候编码.
                        “中文”.getBytes(“UTF-8”);
        字符流:
                设置浏览器打开的时候的编码
                        resposne.setHeader(“Content-Type”,”text/html;charset=UTF-8”);
                设置response的缓冲区的编码
                        response.setCharacterEncoding(“UTF-8”);
                简化的写法:response.setContentType(“text/html;charset=UTF-8”);


        OK,由于篇幅有限,今天我们先讲到这,下一次我们再来讨论一下request乱码和cookie乱码的问题。

评分

参与人数 1黑马币 +5 收起 理由
小疯子ccc + 5 5

查看全部评分

0 个回复

您需要登录后才可以回帖 登录 | 加入黑马