百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程网 > 正文

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

yuyutoo 2024-10-12 01:31 1 浏览 0 评论

前言

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

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,无论输入数据源是网络数据还是本地文件。

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

相关推荐

jQuery VS AngularJS 你更钟爱哪个?

在这一次的Web开发教程中,我会尽力解答有关于jQuery和AngularJS的两个非常常见的问题,即jQuery和AngularJS之间的区别是什么?也就是说jQueryVSAngularJS?...

Jquery实时校验,指定长度的「负小数」,小数位未满末尾补0

在可以输入【负小数】的输入框获取到焦点时,移除千位分隔符,在输入数据时,实时校验输入内容是否正确,失去焦点后,添加千位分隔符格式化数字。同时小数位未满时末尾补0。HTML代码...

如何在pbootCMS前台调用自定义表单?pbootCMS自定义调用代码示例

要在pbootCMS前台调用自定义表单,您需要在后台创建表单并为其添加字段,然后在前台模板文件中添加相关代码,如提交按钮和表单验证代码。您还可以自定义表单数据的存储位置、添加文件上传字段、日期选择器、...

编程技巧:Jquery实时验证,指定长度的「负小数」

为了保障【负小数】的正确性,做成了通过Jquery,在用户端,实时验证指定长度的【负小数】的方法。HTML代码<inputtype="text"class="forc...

一篇文章带你用jquery mobile设计颜色拾取器

【一、项目背景】现实生活中,我们经常会遇到配色的问题,这个时候去百度一下RGB表。而RGB表只提供相对于的颜色的RGB值而没有可以验证的模块。我们可以通过jquerymobile去设计颜色的拾取器...

编程技巧:Jquery实时验证,指定长度的「正小数」

为了保障【正小数】的正确性,做成了通过Jquery,在用户端,实时验证指定长度的【正小数】的方法。HTML做成方法<inputtype="text"class="fo...

jquery.validate检查数组全部验证

问题:html中有多个name[],每个参数都要进行验证是否为空,这个时候直接用required:true话,不能全部验证,只要这个数组中有一个有值就可以通过的。解决方法使用addmethod...

Vue进阶(幺叁肆):npm查看包版本信息

第一种方式npmviewjqueryversions这种方式可以查看npm服务器上所有的...

layui中使用lay-verify进行条件校验

一、layui的校验很简单,主要有以下步骤:1.在form表单内加上class="layui-form"2.在提交按钮上加上lay-submit3.在想要校验的标签,加上lay-...

jQuery是什么?如何使用? jquery是什么功能组件

jQuery于2006年1月由JohnResig在BarCampNYC首次发布。它目前由TimmyWilson领导,并由一组开发人员维护。jQuery是一个JavaScript库,它简化了客户...

django框架的表单form的理解和用法-9

表单呈现...

jquery对上传文件的检测判断 jquery实现文件上传

总体思路:在前端使用jquery对上传文件做部分初步的判断,验证通过的文件利用ajaxFileUpload上传到服务器端,并将文件的存储路径保存到数据库。<asp:FileUploadI...

Nodejs之MEAN栈开发(四)-- form验证及图片上传

这一节增加推荐图书的提交和删除功能,来学习node的form提交以及node的图片上传功能。开始之前需要源码同学可以先在git上fork:https://github.com/stoneniqiu/R...

大数据开发基础之JAVA jquery 大数据java实战

上一篇我们讲解了JAVAscript的基础知识、特点及基本语法以及组成及基本用途,本期就给大家带来了JAVAweb的第二个知识点jquery,大数据开发基础之JAVAjquery,这是本篇文章的主要...

推荐四个开源的jQuery可视化表单设计器

jquery开源在线表单拖拉设计器formBuilder(推荐)jQueryformBuilder是一个开源的WEB在线html表单设计器,开发人员可以通过拖拉实现一个可视化的表单。支持表单常用控件...

取消回复欢迎 发表评论: