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)获取参数进行验证之后程序正常运行.但是,当请求参数在请求体中的时候, ...
随机推荐
- Ubuntu16.04 下创建新用户并赋予sudo权限
https://blog.csdn.net/wales_2015/article/details/79643336
- Java 和 Python 解析动态 key 的 JSON 数据
一.概述 解析JSON过程中,什么情况都可能遇到.遇到特殊的情况,不会怎么办?肯定不是设计的问题,一定是你的姿势不对. 有这样一种JSON需要解析: { "b3444533f6544&quo ...
- 企业级服务元年:iClap高效解决手游更新迭代问题
2006年至今,手游市场经历了不少变革,从WAP站到2009年智能手机时代来临,2012大量资本涌入国内手游行业,到2014年手游市场趋于成熟,细分市场成为追逐热门,在2015年优胜劣汰的资本寒冬浪潮 ...
- bzoj3751 / P2312 解方程
P2312 解方程 bzoj3751(数据加强) 暴力的一题 数据范围:$\left | a_{i} \right |<=10^{10000}$.连高精都无法解决. 然鹅面对这种题,有一种常规套 ...
- 关于《Java读书笔记》第六章课后习题选择题总结与疑问
课后习题 选择题 3 题 代码: class Some{ String ToString(){ return "Some instance"; } } public class M ...
- 20162311 课堂测试 泛型类—Bag
课堂测试 泛型类-Bag 目录 一.题目要求 二.设计思路 三.问题和解决办法 四.代码运行截图 五.代码托管地址 六.总结 一.题目要求 题目:泛型类-Bag 返回目录 二.设计思路 自定义一个Ba ...
- HBuilder 获取通话记录 (Android)
代码: Date.prototype.Format = function (fmt) { var o = { , //月份 "d+": this.getDate(), //日 == ...
- SQL优化:清理生产环境中已失效字段基本步骤
1.统计相应字段的数据情况(如:几年没更新,无数据等情况) 2.确认产品逻辑已无效(产品经理邮件确认) 3.数据备份 4.将数据清空(置为0或空) 5.测试环境中删除引用页面 6.修改定时程序,存储过 ...
- C# 后台模块 Word 模板操作
public static string CreateWord() { //********************************************** //来自博客http://bl ...
- socket可读可写就绪条件
参考 <UNIX 网络编程卷1>中的<第6章 I/O复用> 一. 满足下列四个条件中的任何一个时,一个套接字准备好读. 该套接字接收缓冲区中的数据字节数大于等于套接字接收缓存区 ...