Java中的Inputstream是不能重复读取,这是官方设计的问题。
InputStream实现了三个方法:
public synchronized void mark(int paramInt) {} public synchronized void reset() throws IOException{ throw new IOException("mark/reset not supported"); } public boolean markSupported(){ return false; }
其中,markSupported()方法是直接返回false,而mark()方法中是没有操作的
reset()方法更是直接抛出异常
现在有两种方式可以解决InputStream复用的问题
第一种是利用ByteArrayOutputStream进行缓存
我们写一个InputStreamCache对InputStream进行缓存,已达到我们进行复用的效果。
public class InputStreamCacher { /** * 将InputStream中的字节保存到ByteArrayOutputStream中。 */ private ByteArrayOutputStream byteArrayOutputStream = null; public InputStreamCacher(InputStream inputStream) { byteArrayOutputStream = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int len; try { while ((len = inputStream.read(buffer)) > -1) { byteArrayOutputStream.write(buffer, 0, len); } byteArrayOutputStream.flush(); } catch (IOException e) { } } public InputStream getInputStream() { return new ByteArrayInputStream(byteArrayOutputStream.toByteArray()); } }
用法:
InputStreamCacher cacher = new InputStreamCacher(is);
InputStream stream1 = cacher.getInputStream();
InputStream stream2 = cacher.getInputStream();
看一下ByteArrayInputStream的代码:
public boolean markSupported(){ return true; } public void mark(int paramInt){ mark = pos; } public synchronized void reset(){ pos = mark; }
markSupported返回true,表示支持重置
但是mark中的参数paramInt并没有使用,输入任何值都可以
reset方法可以吧pos直接指向mark标记
所以,其实不需要每次都
InputStream stream1 = cacher.getInputStream();
像这样从缓存中获取ByteArrayInputStream,而是直接
stream1.reset();
先reset后再重新操作stream1就可以了,不需要反复的从缓存cacher里面获取getInputStream
这样会浪费内存。
使用InputStreamCache的缺点也是显而易见的,我们将整个流进行缓存,如果这个缓存很大,是很浪费内存的。
第二种方式,利用BufferedInputStream
BufferedInputStream是支持使用reset的,只需要先mark一下,之后进行reset操作即可。
BufferedInputStream bis = new BufferedInputStream(is);
bis.mark(Integer.MAX_VALUE);设置的标记在重置之后允许读取的字节数是整数的最大值,即Integer.MAX_VALUE,这是为了能够复用整个流的全部内容。
当bis被操作完之后需要再次使用的时候,只要bis.reset();再进行操作即可。
如果不记得怎么使用,我们可以吧BufferedInputStream进行封装一下,写一个流复用类:
public class StreamReuse { private InputStream input; public StreamReuse(InputStream input) { if (!input.markSupported()) { this.input = new BufferedInputStream(input); } else { this.input = input; } } public InputStream getInputStream() { input.mark(Integer.MAX_VALUE); return input; } public void markUsed() throws IOException { input.reset(); } }
当流的接收者使用完流之后,需要显式地调用markUsed方法来发出通知,以完成对流的重置。
StreamReuse sr = new StreamReuse(is);
InputStream is = sr.getInputStream();
xxxxxxx当流被操作完后xxxxxxx
sr.markUsed();
看一下BufferedInputStream的代码:
public synchronized void mark(int paramInt){ marklimit = paramInt; markpos = pos; } public synchronized void reset() throws IOException{ getBufIfOpen(); if (markpos < 0) { throw new IOException("Resetting to invalid mark"); } pos = markpos; } public boolean markSupported(){ return true; }
最新评论