首页 > Request.getInputStrema只能读取一次的分析过程

Request.getInputStrema只能读取一次的分析过程

1. 我们先来看一下继承关系HttpServletRequest 接口继承ServletRequest接口

public abstract interface  ServletRequest

{

public abstract ServletInputStream getInputStream()  throws IOException;

从上面可知request.getInputStream()返回的是ServletInputSteam。查看ServletInputStream源码可知,ServletInputStream extends InputSteam

2.为什么说request.getInputStream()只能读取一次呢,再读一次就没有内容了,这个问题要复习下java的IO知识了。

,在java中读取一个文件或者字符串的内容的代码大家都会写,下边是使用ByteArrayInputStream和ByteArrayOutputStream进行演示:

@Testpublic void testByteArrayInputStream() throws Exception {String str = "AAAAACCCCcCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";//ByteArrayInputStream是把一个byte数组转换成一个字节流,读取的内容是从byte数组中读取的ByteArrayInputStream byteInputStream = new ByteArrayInputStream(str.getBytes());//ByteArrayOutputStream生成对象的时候,是生成一个100大小的byte的缓冲区,写入的时候,是把内容写入内存中的一个缓冲区ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(100);int i =0;byte [] b = new byte[100];while((i = byteInputStream.read(b))!= -1){byteOutput.write(b, 0, i);}System.out.println(new String(byteOutput.toByteArray()));}

打印结果是:AAAAACCCCcCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB

把一个String字符串的内容使用ByteArrayOutputStream读取出来,然后打印显示。这个代码没有什么问题,估计大家都能写出来,但是看一下下边添加一行代码之后的内容:

 

@Testpublic void testByteArrayInputStream() throws Exception {String str = "AAAAACCCCcCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";//ByteArrayInputStream是把一个byte数组转换成一个字节流,读取的内容是从byte数组中读取的ByteArrayInputStream byteInputStream = new ByteArrayInputStream(str.getBytes());//调用这个方法,会影响到下次读取,下次再调用这个方法,读取的起始点会后移5个bytebyteInputStream.read(new byte[5]);//ByteArrayOutputStream生成对象的时候,是生成一个100大小的byte的缓冲区,写入的时候,是把内容写入内存中的一个缓冲区ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(100);int i =0;byte [] b = new byte[100];while((i = byteInputStream.read(b))!= -1){byteOutput.write(b, 0, i);}System.out.println(new String(byteOutput.toByteArray()));}

打印结果是:CCCCcCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB

我在第8行添加了一行代码,这行代码可以把String生成的byte数组中读取5个字节的内容,这行代码会影响到后边第15行byteInputStream的读取结果,显然”AAAAA“在输出中没有了,这是为什么呢?我感觉需要查看java中ByteArrayInputStream的Read方法的实现源码,查看的结果其实可以总结一个句话,就是在InputStream读取的时候,会有一个pos指针,他指示每次读取之后下一次要读取的起始位置,在API文档中是这样解释的:(看过InputStream源码的都明白,read方法其实调用的都是带有三个参数的方法)

public int read(byte[] b,int off, int len)   

 Reads up to len bytes of data into an array of bytes from this input stream. If pos equals count, then -1 is returned to indicate end of file. Otherwise, the number k of

bytes read is equal to the smaller of len and count-pos. If k is positive, then bytes buf[pos] through buf[pos+k-1] are copied into b[off] through b[off+k-1] in the manner

performed by System.arraycopy. The value k is added into pos and k is returned.



 

   就是在每次读取的时候会更新pos的值,当你下次再来读取的时候是从pos的位置开始的,而不是从头开始,所以第二次获取String中的值的时候是不全的,”AAAAA“丢掉了,这也就导致了两次调用request.getInputStream,第二次的时候肯定获取不了值,因为第一次读取完成之后pos指针在末尾,下次再读取肯定读取不到,同request.getInputStream两次调用返回的对象是同一个对象。读取的是同一个Stream。

但是仔细查看API文档你会发现有这样一个方法:

public void reset()
Resets the buffer to the marked position. The marked position is 0 unless another position was marked or an offset was specified in the constructor. 

就是可以把pos的指针的位置重置为起始位置,但是调用它是有条件的,不是所有的IO读取流都能调用这个方法.看一下有这个方法

public boolean markSupported()
Tests if this input stream supports the mark and reset methods. Whether or not mark and reset are supported is an invariant property of a particular input stream instance. 

The markSupported method of InputStream returns false.

这个方法可以判断是不是支持reset()方法的调用,

通过查看ServletInputStream的源码,

ServletInputStream继承了InputStream同时没有重写reset()方法,查看一下InputStream源码,InputStream的reset()方法源码是这样的:

public synchronized void reset() throws IOException {throw new IOException("mark/reset not supported");}

调用reset方法直接抛出异常,所以ServletInputStream是不能调用reset方法,这就导致了只能调用一次getInputStream(),第二次调用的时候没有办法获取到InputStream流中的原因。

现在我们更改一下上边读取String字符串的例子:

 1       @Test2     public void testByteArrayInputStream() throws Exception {3         String str = "AAAAACCCCcCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";4         //ByteArrayInputStream是把一个byte数组转换成一个字节流,读取的内容是从byte数组中读取的5         ByteArrayInputStream byteInputStream = new ByteArrayInputStream(str.getBytes());6         7         //调用这个方法,会影响到下次读取,下次再调用这个方法,读取的起始点会后移5个byte8         byteInputStream.read(new byte[5]);9         byteInputStream.reset();//调用reset方法可以使read中的pos指针复位
10         
11         
12         //ByteArrayOutputStream生成对象的时候,是生成一个100大小的byte的缓冲区,写入的时候,是把内容写入内存中的一个缓冲区
13         ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(100);
14         
15         int i =0;
16         byte [] b = new byte[100];
17         while((i = byteInputStream.read(b))!= -1){
18             byteOutput.write(b, 0, i);
19         }
20         System.out.println(new String(byteOutput.toByteArray()));
21     }


打印结果是:AAAAACCCCcCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB

在第8行添加一行代码,byteInputStream调用reset()方法,打印的结果回复了正常。

转载于:https://www.cnblogs.com/whx7762/p/9475795.html

更多相关:

  • 功能界面 源码: using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; u...

  • cocos2dx 制作单机麻将(二) 打乱麻将顺序2 前面解说了怎样打乱初始给定的麻将牌堆, 另一种是打乱随意给定的麻将牌堆 //混乱扑克2 void RandAppointCardData(BYTE cbCardData[],BYTE cbMaxCount,BYTE OriginalData[]/*源牌堆数据*/) {    ...

  • 原地址:http://www.cnblogs.com/88999660/archive/2013/03/15/2961587.html 保护资源管理文件的相关内容 Unity允许用户使用AssetBundle.CreateFromMemory从一个 byte[]数组中建立一个AssetBundle的对象。在运行传输解密时,可以用这种加...

  • IT发展至今,字符编码版本众多,目前流行的GBK,Unicode,UTF-8编码与汉字的转换可用如下代码:private void button1_Click(object sender, EventArgs e)        {            //汉字转为Unicode编码:            string hz = ...

  • 来源:公众号|计算机视觉工坊(系投稿)作者:仲夏夜之星「3D视觉工坊」技术交流群已经成立,目前大约有12000人,方向主要涉及3D视觉、CV&深度学习、SLAM、三维重建、点云后处理、自动驾驶、CV入门、三维测量、VR/AR、3D人脸识别、医疗影像、缺陷检测、行人重识别、目标跟踪、视觉产品落地、视觉竞赛、车牌识别、硬件选型、学术交流、...

  • 点云PCL免费知识星球,点云论文速读。文章:Real-Time LIDAR-Based Urban Road and Sidewalk Detection for Autonomous Vehicles作者:Ern˝o Horváth  , Claudiu Pozna ,and Miklós Unger编译:点云PCL代码:http...

  • 文章:Semantic Histogram Based Graph Matching for Real-Time Multi-Robot Global Localization in Large Scale Environment作者:Xiyue Guo, Junjie Hu, Junfeng Chen, Fuqin Deng, T...

  • 点云PCL免费知识星球,点云论文速读。文章:Robust Place Recognition using an Imaging Lidar作者:Tixiao Shan, Brendan Englot, Fabio Duarte, Carlo Ratti, and Daniela Rus编译:点云PCL(ICRA 2021)开源代码:...

  • 文章:A Survey of Calibration Methods for Optical See-Through Head-Mounted Displays作者:Jens Grubert , Yuta Itoh, Kenneth Moser编译:点云PCL本文仅做学术分享,如有侵权,请联系删除。欢迎各位加入免费知识星球,获取PD...

  • 原文出处: 韩昊    1 2 3 4 5 6 7 8 9 10 作 者:韩 昊 知 乎:Heinrich 微 博:@花生油工人 知乎专栏:与时间无关的故事   谨以此文献给大连海事大学的吴楠老师,柳晓鸣老师,王新年老师以及张晶泊老师。   转载的同学请保留上面这句话,谢谢。如果还能保留文章来源就更感激不尽了。 我保证这篇文章...

  • 原文出处: 韩昊   我保证这篇文章和你以前看过的所有文章都不同,这是 2012 年还在果壳的时候写的,但是当时没有来得及写完就出国了……于是拖了两年,嗯,我是拖延症患者…… 这篇文章的核心思想就是: 要让读者在不看任何数学公式的情况下理解傅里叶分析。 傅里叶分析不仅仅是一个数学工具,更是一种可以彻底颠覆一个人以前世界观的思维...

  • 很多Linux高手都喜欢使用screen命令,screen命令可以使你轻松地使用一个终端控制其他终端。尽管screen本身是一个非常有用的工具,byobu作为screen的增强版本,比screen更加好用而且美观,并且提供有用的信息和快捷的热键。 想象一下这样一个场景:你通过Secure Shell(ssh)链接到一个服务器,并...

  • NarrowbandPrimary Synchronization Signal时域位置每1个SFN存在一个NPSSSFNSubframeSymbol长度每个SFN5最后11个symbol11个symbols频域位置NB-IOT下行带宽固定180kHz,一个PRB,12个子载波。...

  •  [h1]反斜杠只能够阻止一个字符  [h2]位于键盘的左上角,和~公用一个键。...