背景:前几天总结了文件的上传,本片将从常规下载和多线程下载两个角度去说明如何从网络上下载资源,以及在多线程会遇到的问题和注意点。
1 串行常规下载
思路:1 定位网络资源
2 打开资源链接
3 打开资源输出流
4 利用输入流写如本地文件
public void downFile(){
try {
URL u=new URL(uri);
HttpURLConnection conn=(HttpURLConnection)u.openConnection();
conn.setReadTimeout(5000);
conn.setRequestMethod("GET");
File f=new File(localFile);
RandomAccessFile raf=new RandomAccessFile(f, "rwd");
InputStream is=conn.getInputStream();
byte[] buffer=new byte[1024];
int len=0;
while((len=is.read(buffer)) !=-1 ){
raf.write(buffer,0,len);
}
is.close();
raf.close();
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
注意点: 1 文件名的获取 2 文件输出流的选择有多种
2 多线程下载
思路: 1 定位网络资源
2 获取文件大小
3 根据线程数确定每个线程下载的文件大小
4 多个线程同时去下载
5 合并或者直接写入文件
class DownThread implements Runnable{
private String start;
private String end;
private String localFile;
private String uri;
public DownThread(String uri,String localFile,String start,String end) {
// TODOr Auto-generated constructor stub
this.uri=uri;
this.localFile=localFile;
this.start=start;
this.end=end;
}
@Override
public void run() {
// TODO Auto-generated method stub
try {
URL u=new URL(uri);
HttpURLConnection conn=(HttpURLConnection)u.openConnection();
conn.setReadTimeout(5000);
conn.setRequestMethod("GET");
conn.setRequestProperty("Range", "bytes=" + start + "-" + end);
File f=new File(localFile);
RandomAccessFile raf=new RandomAccessFile(f, "rwd");
raf.seek(Long.parseLong(start));
InputStream is=conn.getInputStream();
byte[] buffer=new byte[1024];
int len=0;
while((len=is.read(buffer)) !=-1 ){
raf.write(buffer,0,len);
}
is.close();
raf.close();
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
注意点: 1 多线程文件和网络资源隔离(保证线程安全)
2 文件大小获取常规采用 conn.getContentLength() ,conn.getContentLengthLong(),两种方法获取, 如果返回-1 ,有以下3种情况 :当获取不到的时候可以采用读取head属性Content-Length的方式;还获取不到有可能服务端做了压缩,此处可以发送指令不让服务端压缩connection.setRequestProperty("Accept-Encoding", "identity"),然后根据返回再次获取; 如果还不能凑效,只能统计出chunk之和,那么此时服务端就不支持多线程下载了。
3 针对每个下载线程发送head头,下载指定的部分 conn.setRequestProperty("Range", "bytes=" + start + "-" + end),有一点需要注意,Range中[start,end]都闭合,所以end最好-1,最后一个线程处理剩余部分。
4 多线程下载控制写文件有两种方式 : 1 采用随机读取写入文件RandomAccessFile,调用seek(start),定位到写的位置, 2 等所有的线程都返回后顺序写文件(此处需要线程协调控制,join(),CountDownLatch......)