首页 > 代码库 > JDK之BufferedInputStream分析

JDK之BufferedInputStream分析

最近写了一个下载文件的功能。

因为文件是在另一台服务器上面,所以要读取网络文件,需用到HttpURLConnection类。

先贴出来代码。

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.*;
public class downloadServlet extends HttpServlet {

 protected void doGet(HttpServletRequest request,HttpServletResponse response)throws ServletException{
    String file=request.getParameter("fileUrl");  //得到网络文件的地址
    response.addHeader("Content-Disposition","attachment;filename='"+file+"'");
    OutputStream os = null;
    InputStream in = null;
    try {
        URL url = new URL(file);
        HttpURLConnection urlConnection = (HttpURLConnection)url.openConnection();
        in = urlConnection.getInputStream();     //获得流
        BufferedInputStream fis = new BufferedInputStream(in);  //BufferedInputStream装饰InputStream
        os = response.getOutputStream();
        int i = -1;
        byte[] b = new byte[1024];
        while((i = fis.read(b, 0, 1024)) != -1){  //读字节
        os.write(b, 0, i);
        }
        fis.close();
        urlConnection = null;
        url = null;
    } catch (IOException e) {
    e.printStackTrace();
    }
    finally {
    try {
        os.close();
            if(in!=null) in.close();
        } catch (IOException e) {
                e.printStackTrace();
        }
    } 
 } 
 
 protected void doPost(HttpServletRequest request,HttpServletResponse response)throws ServletException{ 
	 doGet(request,response); 
 }
}



而其中又用到了BufferedInputStream这个类,然后就研究了一下这个类。

BufferedInputStream"装饰"了InputStream的内部工作方式,使得流的读入操作使用缓冲机制。在使用了缓冲机制后,不会对每一次的流读入操作都产生一个物理的读动作,从而提高了效率。在涉及到物理流的读入时,都应当使用这个装饰流类。

先看一下read方法的源码

    public synchronized int read() throws IOException {
	if (pos >= count) {
	    fill();  //此时需要先去物理位置读一段出来
	    if (pos >= count) //如果仍然没有读出来,则表明已经没有可读的了
		return -1;    //返回-1
	}
	return getBufIfOpen()[pos++] & 0xff;
    }
pos要读的位置,count就是总共有多少个字节


在看一下fill方法

    private void fill() throws IOException {
        byte[] buffer = getBufIfOpen();
	if (markpos < 0)
	    pos = 0;		/* no mark: throw away the buffer */
	else if (pos >= buffer.length)	/* no room left in buffer */
	    if (markpos > 0) {	/* can throw away early part of the buffer */
		int sz = pos - markpos;
		System.arraycopy(buffer, markpos, buffer, 0, sz);
		pos = sz;
		markpos = 0;
	    } else if (buffer.length >= marklimit) {
		markpos = -1;	/* buffer got too big, invalidate mark */
		pos = 0;	/* drop buffer contents */
	    } else {		/* grow buffer */
		int nsz = pos * 2;
		if (nsz > marklimit)
		    nsz = marklimit;
		byte nbuf[] = new byte[nsz];
		System.arraycopy(buffer, 0, nbuf, 0, pos);
                if (!bufUpdater.compareAndSet(this, buffer, nbuf)) {
                    // Can't replace buf if there was an async close.
                    // Note: This would need to be changed if fill()
                    // is ever made accessible to multiple threads.
                    // But for now, the only way CAS can fail is via close.
                    // assert buf == null;
                    throw new IOException("Stream closed");
                }
                buffer = nbuf;
	    }
        count = pos;
	int n = getInIfOpen().read(buffer, pos, buffer.length - pos);
        if (n > 0)
            count = n + pos;
    }
然后注意下这句话n = getInIfOpen().read(buffer, pos, buffer.length - pos);

这就是从物理位置读的

实际上就是调用的InputStream类的read方法,这就是装饰模式的运用

    public int read(byte b[], int off, int len) throws IOException {
	if (b == null) {
	    throw new NullPointerException();
	} else if (off < 0 || len < 0 || len > b.length - off) {
	    throw new IndexOutOfBoundsException();
	} else if (len == 0) {
	    return 0;
	}

	int c = read();
	if (c == -1) {
	    return -1;
	}
	b[off] = (byte)c;

	int i = 1;
	try {
	    for (; i < len ; i++) {
		c = read();
		if (c == -1) {
		    break;
		}
		b[off + i] = (byte)c;
	    }
	} catch (IOException ee) {
	}
	return i;
    }




JDK之BufferedInputStream分析