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进行演示:
@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只能读取一次的分析过程的更多相关文章
- Servlet的Request.getInputStream()只能读取一次问题
Servlet的Request.getInputStream()只能读取一次问题 这个星期公司的项目接口进行改造,公司的接口有的采用了WebService的方式,有的使用的是Http协议+Servle ...
- 解决SpringMVC拦截器中Request数据只能读取一次的问题
解决SpringMVC拦截器中Request数据只能读取一次的问题 开发项目中,经常会直接在request中取数据,如Json数据,也经常用到@RequestBody注解,也可以直接通过request ...
- request.getInputStream() 流只能读取一次问题
问题: 一次开发过程中同事在 sptring interceptor 中获取 request body 中值,以对数据的校验和预处理等操作 .导致之后spring 在读取request body 值做 ...
- httpServletRequest中的流只能读取一次的原因
首先,我们复习一下InputStream read方法的基础知识, java InputStream read方法内部有一个,postion,标志当前流读取到的位置,每读取一次,位置就会移动一次,如果 ...
- 解决spring http输入流和输出流只能读取一次
1.需求:给某些请求接口记录日志,记录请求的数据和响应的数据和请求所花费的时间.这里采用非侵入式编程,也业务代码进行解耦.按照spring AOP 的编程思想. 2.编程设计:在spring 拦截器中 ...
- Spring中如何获取request的方法汇总及其线程安全性分析
前言 本文将介绍在Spring MVC开发的web系统中,获取request对象的几种方法,并讨论其线程安全性.下面话不多说了,来一起看看详细的介绍吧. 概述 在使用Spring MVC开发Web系统 ...
- SpringMVC中使用Ajax POST请求以json格式传递参数服务端通过request.getParameter("name")无法获取参数值问题分析
SpringMVC中使用Ajax POST请求以json格式传递参数服务端通过request.getParameter("name")无法获取参数值问题分析 一:问题demo展示 ...
- 解决HttpServletRequest的输入流只能读取一次的问题
背景 通常对安全性有要求的接口都会对请求参数做一些签名验证,而我们一般会把验签的逻辑统一放到过滤器或拦截器里,这样就不用每个接口都去重复编写验签的逻辑. 在一个项目中会有很多的接口,而不同的接口可能接 ...
- springboot请求体中的流只能读取一次的问题
场景交代 在springboot中添加拦截器进行权限拦截时,需要获取请求参数进行验证.当参数在url后面时(queryString)获取参数进行验证之后程序正常运行.但是,当请求参数在请求体中的时候, ...
随机推荐
- OAuth 白话简明教程 4.刷新 Access Token
转自:http://www.cftea.com/c/2016/11/6705.asp OAuth 白话简明教程 1.简述 OAuth 白话简明教程 2.授权码模式(Authorization Code ...
- Redis在Linux下的安装与配置
Redis是一个开源的使用ANSI C语言编写.支持网络.可基于内存亦可持久化的日志型.Key-Value数据库,并提供多种语言的API. Redis是 NoSQL技术阵营中的一员. 说到NoSQL, ...
- 解决 failed to push some refs to 'git@github.com:zle1992/head-first-java' hint: Updates were rejected because the tip of your curr
问题描述: 寒假之前用实验室电脑push到github 上head first java 的程序,寒假回家后,想用自己的笔记本继续编,继续push . 我先从github下载zip到本地,然后 解压后 ...
- Python 如何判断一个函数是generator函数?
如何判断一个函数是否是一个特殊的 generator 函数?可以利用 isgeneratorfunction 判断: >>>from inspect import isgenerat ...
- bzoj1999 / P1099 树网的核
P1099 树网的核 (bzoj数据加强) 前置知识:树的直径 (并不想贴我的智障写法虽然快1倍但内存占用极大甚至在bzoj上MLE) 正常写法之一:用常规方法找到树的直径,在直径上用尺取法找一遍,再 ...
- 想转行学Java,却又担心自己半路出家成不了大牛
想转行学Java,却又担心自己半路出家成不了大牛 很多人看好Java编程的高薪前景,在自己职业生涯迷茫的时候,想转行学Java,却又担心自己半路出家成不了大牛,赚不到钱,本文就为大家分析一下,转行学J ...
- Spring boot错误处理以及定制错误页面
如果是浏览器访问,返回错误页面 注意浏览器发送请求的请求头: 注意区别其他客户端哦比如 postman 如果是其他客户端,返回一个Json数据 原理可以参照ErrorMvcAutoConfigura ...
- zoj 3747 递推dp
Attack on Titans Time Limit: 2 Seconds Memory Limit: 65536 KB Over centuries ago, mankind faced ...
- UVa 11825 黑客的攻击(状态压缩dp)
https://vjudge.net/problem/UVA-11825 题意: 假设你是一个黑客,侵入了一个有着n台计算机(编号为0,1,...,n-1)的网络.一共有n种服务,每台计算机都运行着所 ...
- HDU 1698 Just a Hook(线段树:区间更新)
http://acm.hdu.edu.cn/showproblem.php?pid=1698 题意:给出1~n的数,每个数初始为1,每次改变[a,b]的值,最后求1~n的值之和. 思路: 区间更新题目 ...