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

  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. //ByteArrayOutputStream生成对象的时候,是生成一个100大小的byte的缓冲区,写入的时候,是把内容写入内存中的一个缓冲区
  8. ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(100);
  9.  
  10. int i =0;
  11. byte [] b = new byte[100];
  12. while((i = byteInputStream.read(b))!= -1){
  13. byteOutput.write(b, 0, i);
  14. }
  15. System.out.println(new String(byteOutput.toByteArray()));
  16.  
  17. }

打印结果是:AAAAACCCCcCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB

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

  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.  
  10. //ByteArrayOutputStream生成对象的时候,是生成一个100大小的byte的缓冲区,写入的时候,是把内容写入内存中的一个缓冲区
  11. ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(100);
  12.  
  13. int i =0;
  14. byte [] b = new byte[100];
  15. while((i = byteInputStream.read(b))!= -1){
  16. byteOutput.write(b, 0, i);
  17. }
  18. System.out.println(new String(byteOutput.toByteArray()));
  19.  
  20. }

打印结果是:CCCCcCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB

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

  1. 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文档你会发现有这样一个方法:

  1. public void reset()
  2. 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读取流都能调用这个方法.看一下有这个方法

  1. public boolean markSupported()
  2. 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()方法源码是这样的:

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

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

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

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

    https://blog.csdn.net/wales_2015/article/details/79643336

  2. Java 和 Python 解析动态 key 的 JSON 数据

    一.概述 解析JSON过程中,什么情况都可能遇到.遇到特殊的情况,不会怎么办?肯定不是设计的问题,一定是你的姿势不对. 有这样一种JSON需要解析: { "b3444533f6544&quo ...

  3. 企业级服务元年:iClap高效解决手游更新迭代问题

    2006年至今,手游市场经历了不少变革,从WAP站到2009年智能手机时代来临,2012大量资本涌入国内手游行业,到2014年手游市场趋于成熟,细分市场成为追逐热门,在2015年优胜劣汰的资本寒冬浪潮 ...

  4. bzoj3751 / P2312 解方程

    P2312 解方程 bzoj3751(数据加强) 暴力的一题 数据范围:$\left | a_{i} \right |<=10^{10000}$.连高精都无法解决. 然鹅面对这种题,有一种常规套 ...

  5. 关于《Java读书笔记》第六章课后习题选择题总结与疑问

    课后习题 选择题 3 题 代码: class Some{ String ToString(){ return "Some instance"; } } public class M ...

  6. 20162311 课堂测试 泛型类—Bag

    课堂测试 泛型类-Bag 目录 一.题目要求 二.设计思路 三.问题和解决办法 四.代码运行截图 五.代码托管地址 六.总结 一.题目要求 题目:泛型类-Bag 返回目录 二.设计思路 自定义一个Ba ...

  7. HBuilder 获取通话记录 (Android)

    代码: Date.prototype.Format = function (fmt) { var o = { , //月份 "d+": this.getDate(), //日 == ...

  8. SQL优化:清理生产环境中已失效字段基本步骤

    1.统计相应字段的数据情况(如:几年没更新,无数据等情况) 2.确认产品逻辑已无效(产品经理邮件确认) 3.数据备份 4.将数据清空(置为0或空) 5.测试环境中删除引用页面 6.修改定时程序,存储过 ...

  9. C# 后台模块 Word 模板操作

    public static string CreateWord() { //********************************************** //来自博客http://bl ...

  10. socket可读可写就绪条件

    参考 <UNIX 网络编程卷1>中的<第6章 I/O复用> 一. 满足下列四个条件中的任何一个时,一个套接字准备好读. 该套接字接收缓冲区中的数据字节数大于等于套接字接收缓存区 ...