Java多线程下载,断点续传

时间:2021-01-04 21:13:28

转载请标明出处:http://blog.csdn.net/wu_wxc/article/details/53706114
本文出自【吴孝城的CSDN博客】

在Java中实现断点续传和多线程下载

package cn.wuxiaocheng;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
public class Download {
private static final String PATH = "http://169.254.217.86:8080/Down/test.exe";
// 假设开了三个线程
private static final int THREADCOUNT = 3;
// 当前正在运行的线程
private static int RUNTHREAD;
public static void main(String[] args) {
try {
HttpURLConnection conn = (HttpURLConnection) new URL(PATH).openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(5000);
// 200代表获取服务器资源全部成功,206代表请求部分资源
int code = conn.getResponseCode();
if (code == 200) {
// 获取要下载的文件大小
int length = conn.getContentLength();
// 把线程的数据赋值给正在运行的线程
RUNTHREAD = THREADCOUNT;
System.out.println("要下载的文件大小为" + length + "字节");
/**
* 创建一个和要下载的文件一样大小的文件,提前把空间申请出来 一般可用来计算空间是否充足,也可不定
* RandomAccessFile 创建可随机访问文件流 rw 参数是打开以便读取和写入,如果文件不存在,则尝试创建
*/

RandomAccessFile raf = new RandomAccessFile(getFileName(PATH), "rw");
// 存入该文件大小
raf.setLength(length);
// 计算每个线程下载的大小
int blockSize = length / THREADCOUNT;
// 计算每个线程开始下载的位置
for (int i = 0; i < THREADCOUNT; i++) {
int startIndex = i * blockSize;
int endIndex = (i + 1) * blockSize - 1;
// 最后一个线程
if (i == THREADCOUNT - 1) {
endIndex = length - 1;
}
// 开始线程去服务器下载东西
DownloadThread downloadThread = new DownloadThread(startIndex, endIndex, i);
downloadThread.start();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
// 定义线程去服务器下载文件
private static class DownloadThread extends Thread {
private int startIndex;
private int endIndex;
private int threadID;
// 通过构造方法将每个线程开始和结束位置传递过来
public DownloadThread(int startIndex, int endIndex, int threadID) {
this.startIndex = startIndex;
this.endIndex = endIndex;
this.threadID = threadID;
}
@Override
public void run() {
try {
HttpURLConnection conn = (HttpURLConnection) new URL(PATH).openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(5000);
// 如果上次有断过,从文件中读取上次下载的位置
File file = new File(getFileName(PATH) + threadID + ".txt");
if (file.exists() && file.length() > 0) {
FileInputStream fios = new FileInputStream(file);
BufferedReader buffer = new BufferedReader(new InputStreamReader(fios));
String lastPositionn = buffer.readLine();
int lastPosition = Integer.parseInt(lastPositionn);
// 改变startIndex的值
startIndex = lastPosition;
System.out.println(threadID + "线程下载的位置:" + startIndex);
fios.close();
}
// 设置一个请求头,告诉服务器每个线程下载的开始和结束位置
conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex);
// 200代表获取服务器资源全部成功,206代表请求部分资源
int code = conn.getResponseCode();
if (code == 206) {
/**
* 创建一个和要下载的文件一样大小的文件,提前把空间申请出来 一般可用来计算空间是否充足,也可不定
* RandomAccessFile 创建可随机访问文件流 rw 参数是打开以便读取和写入,如果文件不存在,则尝试创建
*/

RandomAccessFile raf = new RandomAccessFile(getFileName(PATH), "rw");
raf.seek(startIndex);
// 获得要下载的文件
InputStream in = conn.getInputStream();
// 将数据写入到文件中
int len = 0;
byte[] buffer = new byte[1024 * 1024];
// 定义一个total,记录当前线程下载的位置
int total = 0;
while ((len = in.read(buffer)) != -1) {
raf.write(buffer, 0, len);
// 断点续传,下次下载的时候就从上次的位置开始
total += len;
int currentThreadPosition = startIndex + total;
// 用txt文件存储下载位置
RandomAccessFile raff = new RandomAccessFile(getFileName(PATH) + threadID + ".txt", "rwd");
raff.write(String.valueOf(currentThreadPosition).getBytes());
raff.close();
}
// 关闭流,释放资源
raf.close();
System.out.println("线程" + threadID + "下载完成了");
synchronized (DownloadThread.class) {
RUNTHREAD--;
if (RUNTHREAD == 0) {
// 说明所有的线程都执行完毕,把.txt文件删除
for (int i = 0; i < THREADCOUNT; i++) {
File deleteFile = new File(getFileName(PATH) + i + ".txt");
deleteFile.delete();
}
}
}
}
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
// 获取下载的文件的名字,http://169.254.217.86:8080/Down/test.exe
private static String getFileName(String path) {
int start = path.lastIndexOf("/") + 1;
return path.substring(start);
}
}