客户端多线程断点下载(优化多线程下载) package com.thread;import java.io.File;import java.io.FileInputStream;import java.io.InputStream;import java.io.RandomAccessFile;import java.net.HttpURLConnection;import java.net.URL;/**
package com.thread;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
/**
* 多线程下载的测试类
*
* @author Administrator
*
*/
public class MutiThreadDownloadr {
public static int threadCount = 3; // 线程数量
public static int runningThread = 3; // 运行着的线程数,下面代码会判断逐一减少,直到0
public static void main(String[] args) throws Exception {
// 1.连接服务器,获取一个文件的长度。在本地创建一个大小与服务器文件一样大的临时文件
String path = "http://localhost:8090/jdk.exe";
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();
System.out.println("文件总长度:" + length);
// 在客户端本地创建一个与服务器文件一样大的临时文件
// 需要用到RandomAccessFile的API,可以用指针对应到某块文件的位置上
// 第二个参数:r代表以只读方式打开;
// rw代表读取和写入,数据的更新会先放到缓存中;
// rws代表读取和写入,并且元数据的更新立刻写入底层设备
// rwd代表读取和写入,并且内容的更新立刻写入底层设备
RandomAccessFile raf = new RandomAccessFile("setup.exe", "rwd");
// 指定创建临时文件的长度,应该与服务器上的长度相同
raf.setLength(length);
raf.close(); // 关闭流
// 2.假设3个线程去下载资源
// 平均每一个线程下载的文件的大小
int blocksize = length / threadCount; // 每个线程下载的文件大小
for (int threadid = 1; threadid <= threadCount; threadid++) {
// 每个线程下载的开始位置 ==> (线程id-1)*每一块大小
int startIndex = (threadid - 1) * blocksize;
// 每个线程的结束位置 ==> (线程id*每一块大小)-1
int endIndex = threadid * blocksize - 1;
// 如果线程数量没有被整除,则最后一个线程下载的长度要长一些
if (threadid == threadCount) {
endIndex = length;
}
System.out.println("线程:" + threadid + "下载:--" + startIndex + "-->" + endIndex);
// 4.在主线程中,开启多线程下载
new DownloadThread(threadid, startIndex, endIndex, path).start();
}
} else {
System.out.println("服务器错误");
}
}
/**
* 下载文件的子线程,每一个线程下载对应位置的文件 在子线程中,需要关注4个参数: 1.线程id号 2.起始位置 3.结束位置 4.路径
*
*/
public static class DownloadThread extends Thread {
private int threadId;
private int startIndex;
private int endIndex;
private String path;
// 使用构造方法,初始化以上的参数
public DownloadThread(int threadId, int startIndex, int endIndex, String path) {
this.threadId = threadId;
this.startIndex = startIndex;
this.endIndex = endIndex;
this.path = path;
}
@Override
public void run() {
try {
// =========断点续传:检查是否存在记录下载长度的文件,如果存在读取这个文件的数据
File tempFile = new File(threadId + ".txt");
// 判断只有是文件存在的情况下才断点续传
if (tempFile.exists() && tempFile.length() > 0) {
FileInputStream fis = new FileInputStream(threadId + ".txt");
byte[] temp = new byte[1024];
int leng = fis.read(temp);
String downloadok_length = new String(temp, 0, leng); // 记录已经下载的长度
int downloadokInt = Integer.parseInt(downloadok_length); // 转化为int类型
startIndex = downloadokInt; // 修改下载的真实的开始位置
fis.close();
}
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(5000);
// 重要:::设置Range属性的范围:请求服务器下载部分的文件指明文件的位置
// 格式为:"Range"对应"bytes=开始位置-结束位置"
conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex);
System.out.println("线程真实下载:" + threadId + "下载:--" + startIndex + "-->" + endIndex);
int code = conn.getResponseCode();// 全部资源为200,部分资源为206
if (code == 206) {
// 由于上面设置了Range属性请求的位置
// 那么getInputStream()返回的是当前位置对应的文件的输入流
InputStream is = conn.getInputStream();
RandomAccessFile raf = new RandomAccessFile("setup.exe", "rwd");
// 重要:seek()作用是在写文件的时候,从哪个位置开始写
raf.seek(startIndex); // 每个线程使用startIndex来定位文件
// 循环读InputStream,写入RandomAccessFile中即可
int len = 0;
byte[] buffer = new byte[1024];
// =========断点续传:已经下载的长度
int total = 0;
// =========断点续传:记录当前线程下载的数据长度
while ((len = is.read(buffer)) != -1) {
RandomAccessFile file = new RandomAccessFile(threadId + ".txt", "rwd"); // 线程的临时文件
raf.write(buffer, 0, len);
total += len; // =========断点续传:累计
//System.out.println("线程:" + threadId + "total:" + total);
file.write((total + startIndex + "").getBytes()); // 优化:记录下载的位置
file.close();
}
is.close();
raf.close();
System.out.println("线程:" + threadId + "下载完毕了...");
} else {
System.out.println("线程:" + threadId + "下载失败。。。");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 只要一个线程结束了,就将runningThread减一
runningThread--;
if (runningThread == 0) { // 判断所有线程已经执行完毕了
for (int i = 1; i <= 3; i++) {
File file = new File(i + ".txt");
file.delete();
}
System.out.println("文件下载完毕,删除所有的下载记录");
}
}
}
}
}
