多线程下载,以及断点的实现
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,连接到server,获取一个文件,获取文件的大小跟server的文件一样的暂时文件
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");
// 获取server的返回码
int code = conn.getResponseCode();
// 推断返回码
if (code == 200) {
// 获取返回的长度
int length = conn.getContentLength();
System.out.println("文件总长度:" + length);
// 在client创建出一个跟server大小一致的暂时文件
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");
// 重要:请求server下载部分的文件,指定文件的位置
conn.setRequestProperty("Range", "bytes=" + startIndex + "-"
+ endIndex);
System.out
.println("线程:" + threadId + "真实開始的下载进度:" + startIndex);
// 设置超时时间
conn.setReadTimeout(5000);
// 得到server的状态码,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("文件完成下载,删除进度文件.............");
}
}
}
}
}
posted on 2019-05-12 18:16 xfgnongmin 阅读(89) 评论(0) 编辑 收藏 举报