import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
/**
* 多线程下载,以及断点下载的实现<br>
* 其中有个不好的地方,<br>
* 就是进度文件的保存的时候如果采用RandomAccessFile的方式进行保存的时候<br>
* 虽然会将文件的进度时时的保存在进度文件中,<br>
* 但是,经过实际的测试这样会大大的降低文件的下载的速度,<br>
* 如果采用File和FileOutputStream的话虽然可以加快下载的速度<br>
* 但是进度文件的时时写入会出现问题.<br>
*
* <br>
* 目前我还没有找到很好的解决方式,如果大家有的话欢迎给我留言.
*
* @author MartinDong
*
*/
public class Demo {
// 定义线程个数
public static int threadCount = 3;
// 定义当前存货的线程个数
public static int runningThread = 3;
public static void main(String[] args) throws Exception {
// 1,连接到服务器,获取一个文件,获取文件的大小跟服务器的文件一样的临时文件
String path = "http://172.22.64.193:8080/test.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 raf = new RandomAccessFile("test.exe", "rwd");
// 指定临时文件的大小
raf.setLength(length);
// 释放资源
raf.close();
// 平均每一个线程的文件大小
int blockSize = length / threadCount;
for (int threadId = 1; threadId <= threadCount; threadId++) {
// 线程开始的下载位置
int startIndex = (threadId - 1) * blockSize;
// 线程的结束位置
int endIndex = threadId * blockSize - 1;
// 判断是否是最后一个线程
if (threadId == threadCount) {
// 设置结束的位置为到文件的最后
endIndex = length;
}
System.out.println("线程:" + threadId + "下载:>>>>>>>>"
+ startIndex + ">>>>>>>>>>" + endIndex);
new DownlodeThread(path, threadId, startIndex, endIndex)
.start();
}
}
}
/**
* 下载文件的子线程类,每一个线程下载对应位置文件数据
*
* @author MartinDong
*
*/
public static class DownlodeThread extends Thread {
private String path;
private int threadId;
private int startIndex;
private int endIndex;
/**
*
* @param path
* 文件的下载路径
* @param threadId
* 线程id
* @param startIndex
* 线程开始的位置
* @param endIndex
* 线程结束的位置
*/
public DownlodeThread(String path, int threadId, int startIndex,
int endIndex) {
this.path = path;
this.threadId = threadId;
this.startIndex = startIndex;
this.endIndex = endIndex;
}
@Override
public void run() {
try {
// 检查是否存在下载历史的文件
File tempFile = new File(threadId + ".txt");// =========================断点记录操作===============================
if (tempFile.exists() && tempFile.length() > 0) {
// 文件输入流
FileInputStream fis = new FileInputStream(
tempFile);
// 中间变量,缓存的作用
byte[] tempBuffer = new byte[1024];
// 获取进度文件的数据大小
int length = fis.read(tempBuffer);
// 获取进度文件的数据
String historyData = new String(tempBuffer, 0, length);
// 将进度数据装换为整型
int historyDataInt = Integer.parseInt(historyData);
// 修改真正的下载位置
startIndex = historyDataInt;
fis.close();
}// =========================断点记录操作===============================
// 将地址转换为URL
URL url = new URL(path);
// 获取http连接
HttpURLConnection conn = (HttpURLConnection) url
.openConnection();
// 设置连接的请求方式
conn.setRequestMethod("GET");
// 重要:请求服务器下载部分的文件,指定文件的位置
conn.setRequestProperty("Range", "bytes=" + startIndex + "-"
+ endIndex);
System.out
.println("线程:" + threadId + "真实开始的下载进度:" + startIndex);
// 设置超时时间
conn.setReadTimeout(5000);
// 得到服务器的状态码,200表示请求的全部资源得到响应=== ok,206请求的部分资源得到响应=== ok
int code = conn.getResponseCode();
System.out.println("code:" + code);
if (code == 206) {
// 返回的是指定位置的文件流
InputStream is = conn.getInputStream();
// 创建一个临时的文件
RandomAccessFile raf = new RandomAccessFile("test.exe",
"rwd");
// 移动指针,到指定的文件位置,
raf.seek(startIndex);
// 创建中间缓冲字节数组
byte[] buffer = new byte[1024];
// 读取文件的大小
int length = 0;
// 定义已经下载的数据长度,用作断点下载的记录=========================断点记录操作===============================
int downlodeTotal = 0;
// 循环写入
while ((length = is.read(buffer)) != -1) {
// 定义一个记录线程的记录文件=========================断点记录操作===============================
RandomAccessFile historyFile = new RandomAccessFile(
threadId + ".txt", "rwd");
// 向文件中写入数据
raf.write(buffer, 0, length);
// 记录已经下载的文件长度
downlodeTotal += length;
// 将已经下载的文件长度和开始的读取位置相加,得到已经读取的文件位置
historyFile.write((downlodeTotal + startIndex + "")
.getBytes());
historyFile.close();// =========================断点记录操作===============================
System.out.println("线程:" + threadId + "已下载:"
+ downlodeTotal);
}
is.close();
raf.close();
System.out.println("线程:" + threadId + "下载完毕............");
} else {
System.out.println("线程:" + threadId
+ "下载失败请重新下载............");
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
// 进行线程数量的变化操作
runningThread--;
// 如果当前存活的线程为0,执行进度文件统一销毁的操作
if (runningThread == 0) {
// 如果下载完毕,清除进度文件
for (int threadIndex = 1; threadIndex <= threadCount; threadIndex++) {
// 这里创建的是与线程文件对应的文件,文件名可以自定义,方便起见是采用的是线程的ID表示
File temp = new File(threadIndex + ".txt");
// 执行文件删除的操作
temp.delete();
}
System.out.println("文件下载完毕,删除进度文件.............");
}
}
}
}
}
多线程下载,以及断点的实现,布布扣,bubuko.com
多线程下载,以及断点的实现