首页 > 其他分享 >记一次InputStream流读取不完整留下的惨痛教训

记一次InputStream流读取不完整留下的惨痛教训

时间:2022-12-08 17:00:13浏览次数:35  
标签:读取 read InputStream new 惨痛教训 byte 字节

前言

首先,问问大家下面这段流读取的代码是否存在问题呢?

inputStream = ....
try {
    // 根据inputStream的长度创建字节数组
    byte[] arrayOfByte = new byte[inputStream.available()];
    // 调用read 读取字节数组
    inputStream.read(arrayOfByte, 0, arrayOfByte.length);
    return new String(arrayOfByte);
}catch (Exception e){
    e.printStackTrace();
}

实际上的确是有问题的,而且在线上环境结结实实的坑了我们一把。

问题回溯

  1. 在xx银行项目上,报了下面的一个错误信息,数组越界,如下图所示:

  1. 反编译jar包的代码,在如下位置用到了数组读取,根据=号切割为组数,如下图所示:

  1. 而这个切割的字符串,是调用loadResource方法加载ORG_PATH_MAP得到,如下图所示:

  1. 我们再来看下loadResource的代码:

  1. 这里的是加载ORG_PATH_MAP.class文件的内容,这个文件虽然class,但是里面存储内容的格式如下:
zj=浙江分公司,sh=上海分公司,fz=福州分公司

在我们多次确认数据格式也没有问题以后,就陷入了沉思,大家有发现什么问题呢?

原因分析

我们就怀疑读取的时候是不是有问题,是不是读取得不完整导致得。

我们看了下InputStream类的javadoc:

  1. ****available()

返回可以从此输入流读取(或跳过)的字节数的估计值 ,返回的不是整个数据的长度, 是这次read可读的长度。

InputStream的不同子类对InputStream.available()可能会有不同的实现,一些实现会返回当前可一次无阻塞读入的字节数,另一些实现会返回这个输入流可读入的字节总数, 因此应尽量避免使用该返回值作为开辟能容纳该输入流所有数据的缓冲大小依据。

  1. int read(byte b[], int off, int len)

从输入流中读取最多len字节的数据到字节数组中。尝试读取最多len字节,但可能会读取更小的数字。实际读取的字节数以整数形式返回。

所以做了一个demo试了一下:

  • 有问题的这个项目是用AppClassLoader加载当前路径下的类,可以发现InputStream的实现类是JarURLInputStream

运行结果如下图,可能确实发现读少了。

小结: 在读物流时调用的是available方法,点击进入其源码发现其返回的是当前流可用长度(估计值),不是流的总长度。而在read方法读取流中数据到buffer中,但读取长度为1至buffer.length,若流结束或遇到异常则返回-1。也就是说当实际文件的长度超过此估计可用长度时也不会继续读,而是结束读取。从而导致读取的流并不完整。这很大程度取决于不同的实现。

解决方案

方案一:

 public static byte[] streamToByteArray(InputStream in) throws IOException {
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        byte[] buffer = new byte[4096];
        int n;
        while (-1 != (n = in.read(buffer))) {
            output.write(buffer, 0, n);
        }
        return output.toByteArray();
    }

借助ByteArrayOutputStream,通过循环去读取流,直到读取完成,如果返回-1,表示全部读取完成。

方案二:

public static byte[] streamToByteArray(InputStream in) throws IOException {
        byte[] bytes = new byte[bufferlength];
        BufferedInputStream bis = new BufferedInputStream(is);
        int length = bis.read(bytes, 0, bufferlength)
        return bytes;
    }

采用BufferedInputStream,它底层其实也是循环读取。

为什么测试没发现?

实际情况是我们这是一个公共jar,被不同的组件下载,有的组件放到classpath下通过AppClassloader加载,有的组件通过自定义的classLoader加载,开发测试我们都是用的自定义DynamicClassloader加载,它的InputStream的实现类是ByteInputStream,是没有发现问题的。

而本次是另外一个spark组件, 他们把jar 放到了classpath下 也就是用AppClassloader,最终用了JarURLInputStream读取,出现问题。

总结

  1. 在代码编写过程中,available()方法仅用于估算接收数据的总长度或数据块的长度,不要用于任何需要准确计算的场合,更不要用于开辟一个可以刚好容纳所有数据的缓冲区。
  2. 对于调用InputStream.read(…),务必进行循环调用,直至返回-1,无论输入数据源是网络数据还是本地文件。

在平时的开发过程中,还是需要注重细节,不然会出现意料不到的问题。

如果本文对你有帮助的话,请留下一个赞吧
更多技术干活和学习资料尽在个人公众号——JAVA旭阳

标签:读取,read,InputStream,new,惨痛教训,byte,字节
From: https://www.cnblogs.com/alvinscript/p/16966592.html

相关文章

  • skynet启动时读取配置文件
    新入门skynet系列视频b站网址https://www.bilibili.com/video/BV19d4y1678Xskynet启动时读取配置文件行11skynet启动时,我们启动了一个lua虚拟机,这个虚拟机是在整个......
  • 视频rtsp读取和比对(最后一次发)
    做测试好多年,第一次觉得这个是一个无未来的行业,希望以后的孩子不要再入坑了上个月遇到的一个问题背景:   业务中,用户部署了上千个摄像头,摄像头的视频来源是一个......
  • 大数据-业务数据采集-FlinkCDC 读取 MySQL 数据存入 Kafka
    目录作用app产生各层数据的flink任务bean数据对象common公共常量utils工具类app.ods.FlinkCDC.javapackagecom.atguigu.app.ods;import......
  • 3、electron读取文本文本文件内容展示到页面
    1、项目根目录下创建一个文件文件test.txt2、index.html页面源码:<!DOCTYPEhtml><htmllang="zh-cn"><head><metacharset="utf-8"><metaname="renderer"co......
  • CSV__04--python使用迭代器读取csv文件出现读取结果为空的解决办法
    1迭代器的概念迭代器(iterator)有时又称光标(cursor)是程序设计的软件设计模式,可在容器对象(container,例如链表或数组)上遍访的接口。2迭代器的特点强制性:必须将元素从迭代......
  • MATLAB——读取并处理Excel中的数字、中英文字符
    MATLAB——读取并处理Excel中的数字、中英文字符​​方法1​​​​方法二​​​​方法3​​​​举例​​方法1[data,str]=xlsread(‘1.xlsx’)data=0.80560.81250.8056......
  • python 读取excel 02
    读取文件夹内的所有表,输入想要查询的表名,列出对应表页签,查询对应内容并打印出来importxlrd print('输入‘x’返回上一步!!!') whileTrue:  print('\n输入想要查询的......
  • python 读取excel
    #!/usr/bin/pythonimportxlrd#打开excel表格data_excel=xlrd.open_workbook('macHex-MMQ873-PCL.xls')#获取所有sheet名称names=data_excel.sheet_names()......
  • 高通平台snoop log抓取和蓝牙版本读取
    A.snooplog获取:1.Turnoffbluetooth2.adbshellsetproppersist.bluetooth.btsnoopenabletrue3.adbshellsetproppersist.bluetooth.btsnooplogmodefull(Android......
  • 一文掌握 Go 文件的读取操作
    耐心和持久胜过激烈和狂热。哈喽大家好,我是陈明勇,今天分享的知识是Go文件的读取操作。如果本文对你有帮助,不妨点个赞,如果你是Go语言初学者,不妨点个关注,一起成长一起进步......