笔记如下
-
什么是多线程下载?
- 首先要获得要下载文件的总大小并创建规定大小的空文件
//拿到文件的大小
int length = conn.getContentLength();
//getPathName(path)是得到文件名称
File file = new File(getPathName(path));
//首先创建规定大小的空文件
RandomAccessFile raf = new RandomAccessFile(file, "rw");
raf.setLength(length);
raf.close();
- 每条线程的下载的起始位置于结束位置公式
//去启动线程去下载文件
//threadId :线程的id号
//threadCount :开几条线程----3条
for (int threadId = 0; threadId < threadCount; threadId++) {
int startIndex = threadId*blockSize;
int endIndex = (threadId+1)*blockSize-1;
if (threadId==(threadCount-1)) {
endIndex = length-1;
}
//System.out.println("第"+threadId+"线程:下载 从"+startIndex +"~"+endIndex);
//开启线程下载
new DownloadFilePartThread(threadId, startIndex, endIndex).start();
}
- 通过请求头告诉服务器要下载的内容
conn.setRequestProperty("range", "bytes=" +startIndex+"-"+endIndex);
- 将服务器传回的数据写到文件中
//拿到数据
InputStream in = conn.getInputStream();
//告诉从哪个位子开始写
//raf.seek(startIndex);
int len=0;
byte[] buf = new byte[1024];
while ((len=in.read(buf))>0) {
raf.write(buf,0,len);
//将实时的位置记录下来,方便下面紧接着去往文件中去写
currentPostion=currentPostion+len;
//实现断点下载
File info = new File(threadId+".position");
OutputStream out = new FileOutputStream(info);
//以字符串的值记录当前缓存的配置
//out.write(String.valueOf(currentPostion).getBytes());
out.write((currentPostion+"").getBytes());
out.close();
}
- 实现断点下载
断点下载就是记录上次下载停止的地方,设置为开始地方继续下载------将停止地点记录到文件中.
//将实时的位置记录下来,方便下面紧接着去往文件中去写
currentPostion=currentPostion+len;
//实现断点下载
File info = new File(threadId+".position");
OutputStream out = new FileOutputStream(info);
//以字符串的值记录当前缓存的配置
//out.write(String.valueOf(currentPostion).getBytes());
out.write((currentPostion+"").getBytes());
out.close();
//读取之前已经下载的地方,继续下载
File ilf = new File(threadId+".position");
if (ilf.exists() && ilf.length()>0) {
BufferedReader br = new BufferedReader(new FileReader(ilf));
String vl = br.readLine();
int alreadyWritePosition = Integer.valueOf(vl);
//重新设置http请求头,设置起始下载位置
conn.setRequestProperty("range", "bytes=" +alreadyWritePosition+"-"+endIndex);
//告诉从哪个位子开始写
raf.seek(alreadyWritePosition);
System.out.println("表示之前下载过");
}else{
conn.setRequestProperty("range", "bytes=" +startIndex+"-"+endIndex);
//告诉从哪个位子开始写
raf.seek(startIndex);
System.out.println("表示之前没有下载过");
}
- 文件下载完成后,还要删除记录位置的文件
synchronized (NultiThreadDownload.class) {
currentRunningThread--;
if (currentRunningThread <= 0) {
// 将记录下载位置的文件给删掉
for (threadId = 0; threadId < threadCount; threadId++) {
File fff = new File(threadId + ".position");
fff.renameTo(new File(threadId + ".position.finish"));
File fll = new File(threadId + ".position.finish");
fll.delete();
}
}
}
- 全部源码
public class MultiThreadDownload {
//规定用三条线程去下载
private static int threadCount = 3;
//线程计数器
private static int currentRunningThread = 3;
//private static String path="http://169.254.210.207:8080/file.txt";
private static String path="http://169.254.210.207:8080/ff.exe";
public static void main(String[] args){
//1.向服务器发请求,拿到要下载的文件的长度是多少
try {
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5000);
conn.setRequestMethod("GET");
int code = conn.getResponseCode();
if (code == 200) {
//拿到文件的大小
int length = conn.getContentLength();
//getPathName(path)是得到文件名称
//得到文件路径
File file = new File(getPathName(path));
//首先创建规定大小的空文件
RandomAccessFile raf = new RandomAccessFile(file, "rw");
raf.setLength(length);
raf.close();
//每块线程下载的平均大小
int blockSize = length/threadCount;
//去启动线程去下载文件
//threadId :线程的id号
//threadCount :开几条线程----3条
for (int threadId = 0; threadId < threadCount; threadId++) {
int startIndex = threadId*blockSize;
int endIndex = (threadId+1)*blockSize-1;
if (threadId==(threadCount-1)) {
endIndex = length-1;
}
//System.out.println("第"+threadId+"线程:下载 从"+startIndex +"~"+endIndex);
new DownloadFilePartThread(threadId, startIndex, endIndex).start();
}
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private static class DownloadFilePartThread extends Thread{
//线程的id号
private int threadId;
//线程的开始位置
private int startIndex;
//线程的结束位置
private int endIndex;
//当前线程下载位置
private int currentPostion;
public DownloadFilePartThread(int threadId,int startIndex,int endIndex){
this.threadId = threadId;
this.startIndex = startIndex;
this.endIndex = endIndex;
currentPostion = startIndex;
}
@Override
public void run() {
// TODO Auto-generated method stub
//干下载耗时的事
System.out.println("第"+threadId+"线程:开始下载了..... 从"+startIndex +"~"+endIndex);
try {
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5000);
conn.setRequestMethod("GET");
//在多线程下载中只需要目标文件的一部分的数据
//需要告诉服务器,要下载的内容
//需要设置http请求头可以实现
//startIndex ~ endIndex
conn.setRequestProperty("range", "bytes=" +startIndex+"-"+endIndex);
File file = new File(getPathName(path));
RandomAccessFile raf = new RandomAccessFile(file, "rw");
//读取之前已经下载的地方,继续下载
File ilf = new File(threadId+".position");
if (ilf.exists() && ilf.length()>0) {
BufferedReader br = new BufferedReader(new FileReader(ilf));
String vl = br.readLine();
int alreadyWritePosition = Integer.valueOf(vl);
//重新设置http请求头,设置起始下载位置
conn.setRequestProperty("range", "bytes=" +alreadyWritePosition+"-"+endIndex);
//告诉从哪个位子开始写
raf.seek(alreadyWritePosition);
System.out.println("表示之前下载过");
}else{
conn.setRequestProperty("range", "bytes=" +startIndex+"-"+endIndex);
//告诉从哪个位子开始写
raf.seek(startIndex);
System.out.println("表示之前没有下载过");
}
//多线程下载返回----206
int code = conn.getResponseCode();
if (code == 206) {
//拿到数据
InputStream in = conn.getInputStream();
//告诉从哪个位子开始写
//raf.seek(startIndex);
int len=0;
byte[] buf = new byte[1024];
while ((len=in.read(buf))>0) {
raf.write(buf,0,len);
//将实时的位置记录下来,方便下面紧接着去往文件中去写
currentPostion=currentPostion+len;
//实现断点下载
File info = new File(threadId+".position");
OutputStream out = new FileOutputStream(info);
//以字符串的值记录当前缓存的配置
//out.write(String.valueOf(currentPostion).getBytes());
out.write((currentPostion+"").getBytes());
out.close();
}
in.close();
raf.close();
}
System.out.println("第"+threadId+"线程:下载结束了..... ");
//等到线程完成后再去删文件
//弄一个计数器,记住总共有多少线程下载,每当一个线程下载后就-1
//当计数器小于0或等于0的时候,就说明没有线程在下载了,就删除记录下位置的文件
synchronized (MultiThreadDownload.class) {
currentRunningThread--;
if (currentRunningThread <= 0) {
// 将记录下载位置的文件给删掉
for (threadId = 0; threadId < threadCount; threadId++) {
File fff = new File(threadId + ".position");
fff.renameTo(new File(threadId + ".position.finish"));
File fll = new File(threadId + ".position.finish");
fll.delete();
}
}
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static String getPathName(String path){
int index = path.lastIndexOf("/");
return path.substring(index+1);
}
}