最近写了一个下载文件的功能。
因为文件是在另一台服务器上面,所以要读取网络文件,需用到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(); // 注意一下这句话,就是调用HttpInputStream的read方法
if (c == -1) {
break;
}
b[off + i] = (byte)c;
}
} catch (IOException ee) {
}
return i;
}
然后我们来看一下HttpInputStream的read的方法。
public int read() throws IOException
{
try {
byte[] b = new byte[1];//声明了一个一字节的数组
int ret = read(b); //然后调一下read方法
return (ret == -1? ret : (b[0] & 0x00FF));
} catch (IOException ioex) {
if (cacheRequest != null) {
cacheRequest.abort();
}
throw ioex;
}
}
在看一下HttpInputStream的read(byte[] b)方法
public int read(byte[] b) throws IOException
{
return read(b, 0, b.length);
}
好吧,在看一下HttpInputStream的read(byte[] b,int,int)方法
public int read(byte[] b,
int off,
int len) throws IOException
{
try {
int newLen = super.read(b, off, len);
int nWrite;
// write to cache
if (inCache > 0) {
if (inCache >= newLen) {
inCache -= newLen;
nWrite = 0;
} else {
nWrite = newLen - inCache;
inCache = 0;
}
} else {
nWrite = newLen;
}
if (nWrite > 0 && outputStream != null)
outputStream.write(b, off + (newLen-nWrite), nWrite);
if (marked) {
markCount += newLen;
}
return newLen;
} catch (IOException ioex) {
if (cacheRequest != null) {
cacheRequest.abort();
}
throw ioex;
}
}