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. python中的切片

    python中提供了一种很方便的方法来完成取出指定范围内的元素,这种方法就是切片(Slice). 以下为切片的例子: In [1]: L = ['Michael', 'Sarah', 'Tracy', ...

  2. 234. Palindrome Linked List(判断链表是否回文)

    Given a singly linked list, determine if it is a palindrome. Follow up:Could you do it in O(n) time ...

  3. 2018-2019 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2018) Solution

    A. Altruistic Amphibians Upsolved. 题意: $有n只青蛙,其属性用三元组表示 <l_i, w_i, h_i> l_i是它能跳的高度,w_i是它的体重,h_ ...

  4. NOIP 2018 兔纸旅游记

    今年是第一次参加tg呢... Day0    早上出发去中旅坐大巴,走有 lz 特色的OI比赛道路. 车上谈笑风生,看 jw 的 GDOI 的小本本. 到动车站取票入站,看 lmh 和 zn 的爱恨情 ...

  5. Mac下配置Hive环境

    在配置Hive环境之前,需要Hadoop环境. 安装Hive 点击下载 下载结束后,会有一个.tar文件,使用以下命令解压该文件. tar -zxvf 要解压的tar包 解压完成后如下 修改Hive配 ...

  6. 使用IDEA2017创建java web +maven项目

    1.首先,提前准备的东西:我使用的是IDEA2017,tomcat7,jdk1.:请将这些软件安装完成,环境变量配置完成,如何配置以及安装我就不复述了,百度上一堆一堆的,比我知道的详细多了. 2.下面 ...

  7. python批量修改ssh密码

    由于工作需要本文主结合了excel表格,对表格中的ssh密码进行批量修改 以下是详细代码(python3): #!/usr/bin/env python#-*-coding:utf-8-*- impo ...

  8. 网络安全:攻击和防御练习(全战课), DDos压力测试

    XSS 跨站脚本攻击: Cross-site scripting(简称xss)跨站脚本. 一种网站的安全漏洞的攻击,代码注入攻击的一种.XSS攻击通常指的是通过利用网页开发时留下的漏洞,通过巧妙的方法 ...

  9. Leetcode 12

    //日积月累,水滴石穿class Solution { public: string longestCommonPrefix(vector<string>& strs) { if ...

  10. poj 2029 Get Many Persimmon Trees 各种解法都有,其实就是瞎搞不算吧是dp

    连接:http://poj.org/problem?id=2029 题意:给你一个map,然后在上面种树,问你h*w的矩形上最多有几棵树~这题直接搜就可以.不能算是DP 用树状数组也可作. #incl ...