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进行演示:

@Test
public 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读取出来,然后打印显示。这个代码没有什么问题,估计大家都能写出来,但是看一下下边添加一行代码之后的内容:

@Test
public void testByteArrayInputStream() throws Exception {
String str = "AAAAACCCCcCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";
//ByteArrayInputStream是把一个byte数组转换成一个字节流,读取的内容是从byte数组中读取的
ByteArrayInputStream byteInputStream = new ByteArrayInputStream(str.getBytes()); //调用这个方法,会影响到下次读取,下次再调用这个方法,读取的起始点会后移5个byte
byteInputStream.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       @Test
2 public void testByteArrayInputStream() throws Exception {
3 String str = "AAAAACCCCcCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";
4 //ByteArrayInputStream是把一个byte数组转换成一个字节流,读取的内容是从byte数组中读取的
5 ByteArrayInputStream byteInputStream = new ByteArrayInputStream(str.getBytes());
6
7 //调用这个方法,会影响到下次读取,下次再调用这个方法,读取的起始点会后移5个byte
8 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()方法,打印的结果回复了正常。

Request.getInputStrema只能读取一次的分析过程的更多相关文章

  1. Servlet的Request.getInputStream()只能读取一次问题

    Servlet的Request.getInputStream()只能读取一次问题 这个星期公司的项目接口进行改造,公司的接口有的采用了WebService的方式,有的使用的是Http协议+Servle ...

  2. 解决SpringMVC拦截器中Request数据只能读取一次的问题

    解决SpringMVC拦截器中Request数据只能读取一次的问题 开发项目中,经常会直接在request中取数据,如Json数据,也经常用到@RequestBody注解,也可以直接通过request ...

  3. request.getInputStream() 流只能读取一次问题

    问题: 一次开发过程中同事在 sptring interceptor 中获取 request body 中值,以对数据的校验和预处理等操作 .导致之后spring 在读取request body 值做 ...

  4. httpServletRequest中的流只能读取一次的原因

    首先,我们复习一下InputStream read方法的基础知识, java InputStream read方法内部有一个,postion,标志当前流读取到的位置,每读取一次,位置就会移动一次,如果 ...

  5. 解决spring http输入流和输出流只能读取一次

    1.需求:给某些请求接口记录日志,记录请求的数据和响应的数据和请求所花费的时间.这里采用非侵入式编程,也业务代码进行解耦.按照spring AOP 的编程思想. 2.编程设计:在spring 拦截器中 ...

  6. Spring中如何获取request的方法汇总及其线程安全性分析

    前言 本文将介绍在Spring MVC开发的web系统中,获取request对象的几种方法,并讨论其线程安全性.下面话不多说了,来一起看看详细的介绍吧. 概述 在使用Spring MVC开发Web系统 ...

  7. SpringMVC中使用Ajax POST请求以json格式传递参数服务端通过request.getParameter("name")无法获取参数值问题分析

    SpringMVC中使用Ajax POST请求以json格式传递参数服务端通过request.getParameter("name")无法获取参数值问题分析 一:问题demo展示 ...

  8. 解决HttpServletRequest的输入流只能读取一次的问题

    背景 通常对安全性有要求的接口都会对请求参数做一些签名验证,而我们一般会把验签的逻辑统一放到过滤器或拦截器里,这样就不用每个接口都去重复编写验签的逻辑. 在一个项目中会有很多的接口,而不同的接口可能接 ...

  9. springboot请求体中的流只能读取一次的问题

    场景交代 在springboot中添加拦截器进行权限拦截时,需要获取请求参数进行验证.当参数在url后面时(queryString)获取参数进行验证之后程序正常运行.但是,当请求参数在请求体中的时候, ...

随机推荐

  1. 什么是Java泛型?

    ①为什么要使用泛型? 引入例子 public class GenericTest { public static void main(String[] args) { List list = new ...

  2. IBM究竟是一家怎样的公司

    每次被问到这样的“简单”问题,我都很纠结: 这家公司,从创始至今已经积累了几十万种技术(2015年蝉联专利排行榜23年之久,仅2015年专利数7355项),开发了上万种产品(从银行的交易系统,到航空的 ...

  3. ipod不识别命令

    root@mx6q:~# history 0 sync 1 reboot 2 cd /usr/app/ 3 ls 4 cd extend-sysfs/ 5 ls 6 cd etc/ 7 ls 8 cd ...

  4. scp命令简单应用

    实例1:从远处复制文件到本地目录 $scp root@10.6.159.147:/opt/soft/demo.tar /opt/soft/ 说明: 从10.6.159.147机器上的/opt/soft ...

  5. JavaWeb实现分页的四种方法

    一.借助数组进行分页 原理:进行数据库查询操作时,获取到数据库中所有满足条件的记录,保存在应用的临时数组中,再通过List的subList方法,获取到满足条件的所有记录. 实现: 首先在dao层,创建 ...

  6. G_M_C_美食节

    美食节 题解:学习了动态加边,可以说是进一步理解了网络流.具体思路就是考虑每一道菜,如果这是该位厨师最后一次做,那么等待时间就是做这道菜的时间,如果是倒数第二次做,就要两倍时间(目前做了一次,后面还有 ...

  7. JS localStorage 存储变量

    if(!window.localStorage && /MSIE/.test(navigator.userAgent)){ if(!window.UserData) { window. ...

  8. 【源码学习之spark core 1.6.1 各种部署模式所使用的的TaskSceduler及SchedulerBackend】

    说明:个人原创,转载请说明出处 http://www.cnblogs.com/piaolingzxh/p/5656879.html 未完待续 未完待续

  9. [翻译]PyMongo官方文档

    PyMongo官方文档翻译 周煦辰 2016-06-30 这是本人翻译的PyMongo官方文档.现在网上分(抄)享(袭)的PyMongo博客文章很多,一方面这些文章本就是抄袭的,谈不上什么格式美观,另 ...

  10. TTL的具体含义

    TTL(Time To Live)生存时间值,在IP数据包从源到目的的整个转发路径上,每经过一个路由器,路由器都会修改这个TTL字段值,具体的做法是把该TTL的值减1,然后再将IP包转发出去.如果在I ...