一,下载项目分析:
1,单线程下载:
网络 流
2,多线程下载
a,每个线程下载多少数据。 文件总大小%线程数==0? 文件总大小%线程数:文件总大小/线程数+1;
b,某个请从资源 的某个位置下载 => http协议请求头 Range: byte=xxx-xx
c,在下载前,取出要下载的文件的大小,并在本地建立一个同大小的文件。
RanddomAccessFile
setFileSize();
d,如何让线程找到文件的指定位置写入
RanddomAccessFile
seek(int position)
3,断点续传
a,将没下完的文件记录到 属性文件/XML文件
4,迅雷最新的技术;
多服务器下载,从不同的服务器找到文件源进行下载。 vip客户
这个项目主要是实现了第2个方案:
XunleiTest类:
这个是测试类,设置了参数:
URL: 下载的目的地址
threadCount: 线程数
DownLoadManager: 创建的下载管理类
然后调用其download()方法:
值得一提的是在调用这个方法时,同时实现了OnSizeChanged接口
这个接口是用来实时显示已经下载了多少数据,即进度是多少,
采用的是回调函数的形式。
里面是靠total不断的累加得到的数据和,
这个total要定义成全局静态的,要不就达不到数据变化的效果
DownLoadManager类:
download()方法:
参数说明 url:要下载的文件的url threadCount :线程数 Directory:保存的目录位置 OnSizeChanged:下载的数据量
1,取出文件大小 getFileSize(url):
A,通过 openConnection()建立连接
B,注意:在此方法中因为只需要知道获得文件的大小,
所以setRequestMethod()设置为HEAD
C,getResponseCode()获取服务器的响应码
要是为200,则表示访问成功
D,getContentLength()接受其文件的大小
2, 将下载的文件重新命名 genFileName():
A,取到当前的时间到秒
B,通过url.getFile()取到下载的文件的名字,通过截取的方式保留文件的后缀名
3,创建文件:
gen():用来创建一个新的文件带保存地址和名字如果有保存的地址则
如果有保存的地址则用有的,如果没有则通过System.getProperty("user.dir");
用系统默认的。
createSaveFile():用来在划分一个空间,用来以后接受下载时接受数据;
//RandomAccessFile:此类的实例支持对随机访问文件的读取和写入.
随机访问文件的行为类似存储在文件系统中的一个大型 byte 数组
//"rwd" 打开以便读取和写入,对于 "rw",还要求对文件内容的每个更新都同步写入到底层存储设备。
RandomAccessFile raf=new RandomAccessFile(saveFile, "rwd");
4:每个线程需要下载的长度 getSizePerThread();
文件总大小%线程数==0? 文件总大小%线程数:文件总大小/线程数+1
5,根据线程数threadCount启动线程 DownLoadService
并将这些参数传过去:
URL:下载地址
i: 当前线程
sizePerThread:该线程要下载的长度
savefile:保存的文件
onSizeChanged:下载的变化量
DownLoadService类是一个实现了Runnable接口的一个类:
run():
A,计算当前的线程的起始位置 start
B,计算当前的线程的终点位置 end
C,建立与资源的连接:
这里的setRequestMethod()用"GET",因为要正式下载其内容了
注意:
需要设置下载时的长度
//设置一般请求属性 键 值
setRequestProperty("Range", "bytes="+start+"-"+end)
//HTPP属性: Range 字节范围
D,用字节输入流接受数据 InputStream
E,创建RandomAccessFile
注意:为了能点对点的将数据精确插入
这里使用了RandomAccessFile.seek(start);
F,进入循环将流写到本地,
并且其实回传onSizeChanged即显示的进度量
具体代码:
package com.yc.bean3.xunleixaizai;
import java.io.IOException;
import java.net.URL;
public class XunleiTest {
static long total=0;
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
URL url=new URL("http://yze.t.sogou.com/installpage/sogougmoh.e?dn=ThunderSpeed_1.0.32.350_XiaZaiBa_sgdl.exe");
int threadCount=4;
DownLoadManager dlm=new DownLoadManager();
dlm.download(url, threadCount, null, new OnSizeChanged(){
@Override
public void nofiySizeChange(long size) {
total+=size;
System.out.println("已经下载了:"+total+"字节");
}
});
}
package com.yc.bean3.xunleixaizai;
public interface OnSizeChanged {
public void nofiySizeChange(long size);
package com.yc.bean3.xunleixaizai;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DownLoadManager {
//下载方法: url:要下载的文件的url threadCount :线程数 Directory:保存的目录位置
public void download(URL url,int threadCount,File Directory, OnSizeChanged onSizeChanged) throws IOException{
long filesize=-1;
try {
//1取出文件大小
filesize=getFileSize(url);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//2,取出文件名
String filename=genFileName(url);
//3创建文件
File savefile=gen(filename, Directory);
createSaveFile(savefile, filesize);
//求出每个线程要下载的长度
long sizePerThread=getSizePerThread(filesize, threadCount);
//4根据 线程数据启动线程
for(int i=0;i<threadCount;i++){
Thread t=new Thread(new DownLoadService(url,i,sizePerThread,savefile,onSizeChanged));
t.start();
}
}
//求出每个线程要下载的文件长度
private long getSizePerThread(long filesize,int threadCount){
return filesize%threadCount==0? filesize/threadCount: filesize/threadCount+1;
}
//获取要下载的文件大小
private long getFileSize(URL url) throws IOException{
long filesize=-1;
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setConnectTimeout(40000); //保持与服务器的连接的时间
con.setRequestMethod("HEAD");//注意:请求方式不同,返回的流的数据不同
con.connect();
int code=con.getResponseCode(); //获取服务器的响应码
if(code==200){
filesize=con.getContentLength();
}
return filesize;
}
//在磁盘上创建保存文件
//saveFile:要保存的文件的全路径
//filesize:文件的大小
private void createSaveFile(File saveFile,long filesize) throws IOException{
//RandomAccessFile:此类的实例支持对随机访问文件的读取和写入. 随机访问文件的行为类似存储在文件系统中的一个大型 byte 数组
//"rwd" 打开以便读取和写入,对于 "rw",还要求对文件内容的每个更新都同步写入到底层存储设备。
RandomAccessFile raf=new RandomAccessFile(saveFile, "rwd");
raf.setLength(filesize);
raf.close();
}
//savefilename:要保存的文件名
//savedirectory: 要保存的目录,如果为空的话,则系统指定目录
private static File gen(String savefilename, File savedirectory) {
File f=null;
if(savedirectory==null){
f=new File(System.getProperty("user.dir"),savefilename);
}else{
f=new File(savedirectory,savefilename);
}
return f;
}
//从url中取后缀名 : yyyyMMddHHmmss.原文件的后缀名
private static String genFileName(URL url){
Date d=new Date();
SimpleDateFormat sdf=new SimpleDateFormat("yyyyMMddHHmmss");
String filename=sdf.format(d);
//从url中取后缀名
String suffix=url.getFile().substring(url.getFile().lastIndexOf("."));
filename+=suffix;
return filename;
}
package com.yc.bean3.xunleixaizai;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.ProtocolException;
import java.net.URL;
public class DownLoadService implements Runnable {
private URL url;
private int i;
private long sizePerThread;
private File savefile;
private boolean flag;
private OnSizeChanged onSizeChanged;
public DownLoadService(URL url,int i, long sizePerThread,File savefile, OnSizeChanged onSizeChanged){
super();
this.url=url;
this.i=i;
this.sizePerThread=sizePerThread;
this.savefile=savefile;
flag=true;
this.onSizeChanged=onSizeChanged;
}
@Override
public void run() {
//4计算当前的线程的起始位置
long start=i*sizePerThread;
//5计算当前的线程的终点位置
long end=(i+1)*sizePerThread-1;
InputStream iis=null;
//1,建立与资源的连接
HttpURLConnection con;
try {
con = (HttpURLConnection) url.openConnection();
con.setConnectTimeout(40000); //保持与服务器的连接的时间
con.setRequestMethod("GET");//注意:请求方式不同,返回的流的数据不同
con.setRequestProperty("Range", "bytes="+start+"-"+end);
con.connect();
iis=con.getInputStream();
} catch (ProtocolException e2) {
// TODO Auto-generated catch block
e2.printStackTrace();
} catch (IOException e2) {
// TODO Auto-generated catch block
e2.printStackTrace();
}
//2创建输入流
//3创建RandomAccessFile
//RandomAccessFile:此类的实例支持对随机访问文件的读取和写入. 随机访问文件的行为类似存储在文件系统中的一个大型 byte 数组
//"rwd" 打开以便读取和写入,对于 "rw",还要求对文件内容的每个更新都同步写入到底层存储设备。
RandomAccessFile raf = null;
try {
raf = new RandomAccessFile(savefile, "rwd");
//给raf定位
raf.seek(start);//设置到此文件开头测量到的文件指针偏移量,在该位置发生下一个读取或写入操作
} catch (Exception e2) {
e2.printStackTrace();
}
while(flag){
byte[] bs=new byte[1024];
int length=-1;
try {
while((length=iis.read(bs,0,bs.length))!=-1){
raf.write(bs,0,length);
if(this.onSizeChanged!=null){
this.onSizeChanged.nofiySizeChange(length);
}
}
flag=false;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}