项目上线一周后,正准备看新闻的我突然接到了一个任务。线上突然出现了一条乱码的数据,需要解决这个bug。于是我放下了手中的保温杯,开始解决这个bug。经过一番折腾,发现是有一个同事在处理IO流上写得有点问题,导致了乱码的产生。

一、问题的发现与分析

(1)发现
    针对这个乱码问题,我脑海中闪过了3种会导致乱码产生的情景。
      • [1] 数据库表里面字符集设置错误
      • [2] 由于未加编码过滤器导致SpringMVC接收参数时造成的乱码
      • [3] 代码中涉及byte数组转换String时出现了问题
    经过一序列的排查,发现不存在 [1] [2] 的问题,应该是 [3] 这种场景出现了问题。
    经过仔细阅读代码,发现了一个InputStream流转成String字符串的代码有bug,会导致出现乱码。代码如下图
                
    防止图片失效,代码也贴上
	/**
* 将流中的内容转换为字符串,主要用于提取request请求的中requestBody
* @param in
* @param encoding
* @return
*/
public static String streamToString(InputStream in, String encoding){
// 将流转换为字符串
try {
StringBuffer sb = new StringBuffer();
byte[] b = new byte[1024];
for (int n; (n = in.read(b)) != -1;) {
sb.append(new String(b, 0, n, encoding));
}
return sb.toString();
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("提取 requestBody 异常", e);
}
}
20
 
1
    /**
2
     * 将流中的内容转换为字符串,主要用于提取request请求的中requestBody
3
     * @param in
4
     * @param encoding
5
     * @return
6
     */
7
    public static String streamToString(InputStream in, String encoding){
8
        // 将流转换为字符串
9
        try {
10
            StringBuffer sb = new StringBuffer();
11
            byte[] b = new byte[1024];
12
            for (int n; (n = in.read(b)) != -1;) {
13
                sb.append(new String(b, 0, n, encoding));
14
            }
15
            return sb.toString();
16
        }  catch (IOException e) {
17
            e.printStackTrace();
18
            throw new RuntimeException("提取 requestBody 异常", e);
19
        }
20
    }
(2)分析
    这段代码是一个字节流读取内容,然后转换成String的过程。仔细观察他这段代码,发现将流的内容读取进来是采用小数组的方式。小数组读取的方式本身没什么问题,但是下面的这个new String这个代码就有大问题了。java中utf-8编码的中文是占3个字节。如果刚好有一个中文"我"字处于流中的位置为第1023-1025字节,那么采用小数组方式第一次读取时只读到了这个"我"字的2/3,把这2/3转成String时就产生了乱码。
    因此,根本原因是用小数组方式会出现读到半个中文,然后把这个半个中文转成String就会乱码。要解决这个问题,只需要将所有数据都读进来,最后再转换成String即可。

二、问题的解决

    经过上面的分析,我们知道如果要保证不出现乱码则必须将流数据全部读取完毕再转换成String。为了实现这个功能,那这个byte小数组怎么合并呢?一次性全部读进来感觉也不是很好的方案。这时候轮到内存输出流ByteArrayOutputStream登场了。具体的直接看下面代码
	/**
* 将流中的内容转换为字符串,主要用于提取request请求的中requestBody
* @param in
* @param encoding
* @return
*/
public static String streamToString(InputStream in, String encoding){
// 将流转换为字符串
ByteArrayOutputStream bos = null;
try {
// 1.创建内存输出流,将读到的数据写到内存输出流中
bos = new ByteArrayOutputStream();
// 2.创建字节数组
byte[] arr = new byte[1024];
int len;
while(-1 != (len = in.read(arr))) {
bos.write(arr, 0, len);
}
// 3.将内存输出流的数据全部转换为字符串
return bos.toString(encoding);
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("提取 requestBody 异常", e);
} finally {
if(null != bos) {
try {
// 其实这个内存输出流可关可不关,因为它的close方法里面没做任何操作。
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
 
1
    /**
2
     * 将流中的内容转换为字符串,主要用于提取request请求的中requestBody
3
     * @param in
4
     * @param encoding
5
     * @return
6
     */
7
    public static String streamToString(InputStream in, String encoding){
8
        // 将流转换为字符串
9
        ByteArrayOutputStream bos = null;
10
        try {
11
            // 1.创建内存输出流,将读到的数据写到内存输出流中
12
            bos = new ByteArrayOutputStream();
13
            // 2.创建字节数组
14
            byte[] arr = new byte[1024];
15
            int len;
16
            while(-1 != (len = in.read(arr))) {
17
                bos.write(arr, 0, len);
18
            }
19
            // 3.将内存输出流的数据全部转换为字符串
20
            return bos.toString(encoding);
21
        }  catch (IOException e) {
22
            e.printStackTrace();
23
            throw new RuntimeException("提取 requestBody 异常", e);
24
        } finally {
25
            if(null != bos) {
26
                try {
27
                    // 其实这个内存输出流可关可不关,因为它的close方法里面没做任何操作。
28
                    bos.close();
29
                } catch (IOException e) {
30
                    e.printStackTrace();
31
                }
32
            }
33
        }
34
    }

三、小结

    在将字节流内容转换成字符串时,特别要注意这种读取到半个中文的问题。

记一次InputStream引起的乱码的更多相关文章

  1. InputStream 读取中文乱码 扩展

    对于InputStream读取中文乱码,下面这段话给出了很好的解释,以及后续编码上的扩展. BufferedInputStream和BufferedOutputStream是过滤流,需要使用已存在的节 ...

  2. C# InputStream获取后乱码处理

    Post推送过来的数据流获取后部分中文出现乱码,晚上找了好多办法,不如朋友鼎力相助,哈哈哈~不说废话了上代码把 旧代码基本是网上普遍写法,字段不长用起来不会有乱码情况,但是传送字段一旦过长,超过byt ...

  3. Android InputStream接收 字符串乱码 问题

    各个国家和地区所制定的不同 ANSI 编码标准中,都只规定了各自语言所需的“字符”.比如:汉字标准(GB2312)中没有规定韩国语字符怎样存储.这些 ANSI 编码标准所规定的内容包含两层含义:1. ...

  4. 记一次IntelliJ IDEA中文乱码问题

    问题描述:输出控制台中文乱码,反正就是各种百度解决不了 问题解决:https://blog.csdn.net/m0_37893932/article/details/78280663 解决方案:我用的 ...

  5. 记一次Win上MySQL乱码问题

    Win上MySQL乱码问题 笔记本上的数据库总会时不时的乱码(或者是一直乱码我没注意到?),在谷歌上试了几次错才正确解决,在此记录一下. 在MySQL数据库存储目录找到my.ini,在相应的标签下分别 ...

  6. 记一次idea后台日志乱码解决办法

  7. javaSE高级篇2 — 流技术 — 更新完毕

    1.先认识一个类----File类 前言:IO相关的一些常识 I / O----输入输出 I     输入     input 0    输出     output I / o 按数据的流动方向来分- ...

  8. JavaFX+SpringBoot+验证码功能的小型薪酬管理系统

    2020.07.22更新 1 概述 1.1 简介 一个简单的小型薪酬管理系统,前端JavaFX+后端Spring Boot,功能倒没多少,主要精力放在了UI和前端的一些逻辑上面,后端其实做得很简单. ...

  9. java web 学习十(HttpServletRequest对象1)

    一.HttpServletRequest介绍 HttpServletRequest对象代表客户端的请求,当客户端通过HTTP协议访问服务器时,HTTP请求头中的所有信息都封装在这个对象中,通过这个对象 ...

随机推荐

  1. Android开发学习之RecyclerView

    1.在app/build.gradle中添加RecyclerView依赖 implementation 'com.android.support:recyclerview-v7:28.0.0' 注意依 ...

  2. Android Studio--gradle:download 过慢甚至超时timeout报错

    问题描述 今天第一次学习安卓,配置环境花了不少时间其他都比较容易的解决了 gradle 因为会从外国的网站下载东西会非常的慢(具体原理我也不清楚),所以最好让下载地址变成国内的 解决方案(最后有升级方 ...

  3. 使用 float 存储小数?

    很多程序员就会使用 float 类型来存储小数.sql 的 float 类型和其他大多数编程语言的 float 类型一样, 根据IEEE 754 标准使用二进制格式编码实数数据. 但是很多程序员并不清 ...

  4. CSS布局---居中方法

    在web页面布局中居中是我们常遇到的情况,而居中分为水平居中与垂直居中 文本的居中 CSS中对文本的居中做的非常友好,我们是需要text-align, line-height 两个属性就可以控制文本的 ...

  5. Python 反射机制之hasattr()、getattr()、setattr() 、delattr()函数

    反射机制 先看看我对Java中反射机制的通俗理解:反射之中包含了一个“反”的概念,所以要想解释反射就必须先从“正”开始解释,一般而言,当用户使用一个类的时候,应该先知道这个类,而后通过这个类产生实例化 ...

  6. 产品经理说| AIOps 让告警变得更智能 (下)

    AIOps 人工智能和IT运营支撑 Ops 之间的故事,愈演愈烈,已经成为当今运维圈的热门话题,我打算从2篇文档分享我们在 AIOps 上一些探索和实践.(上篇)主要介绍了为什么事件(告警)处理需要 ...

  7. 适配器Adapter

    #!/usr/bin/env python # -*- coding: utf-8 -*- # @Time : 2019/3/4 22:13 # @Author : ChenAdong # @emai ...

  8. MSSQL coalesce系统函数简介

    转自:http://www.maomao365.com/?p=4390 一.coalesce函数简介 coalesce 系统函数,比ISNULL更强大,更方便的系统函数,coalesce可以接收多个参 ...

  9. SQL Server 2016新特性: 对JSON的支持

     SQL Server 2005开始支持XML数据类型,提供原生的XML数据类型.XML索引及各种管理或输出XML格式的函数.随着JSON的流行,SQL Server2016开始支持JSON数据类 ...

  10. kafka_2.11-2.0.0_常用操作

    参考博文:Kafka消费组(consumer group) 参考博文:kafka 1.0 中文文档(九):操作 参考博文:kafka集群管理工具kafka-manager部署安装 以下操作可以在min ...