一、断点下载:
1.使用HttpURLConnection实现断点下载。
①通过服务器资源路径来得到URL。
②通过URL得到HttpURLConnection对象,并且设置发送请求方式和请求参数等。
③得到资源文件的大小和线程个数,创建一个相同大小的RandomAccessFile文件。
④ 得到每个线程需要下载的区块大小,计算每个线程对应的下载的位置(startIndex~endIndex)。
⑤创建一个文件记录已经下载资源的总大小,当下次下载时,读取该文件的内容,即可从上次下载之后的位置开始下载。
⑥创建并开启线程。
⑦全部线程下载完毕后,删除记录位置的文件。
2.断点下载的细节:
①RandomAccessFile的mode有"r","rw","rws","rwd"四种,其中"r","rw"是先将更新写入存储设备的缓冲区,若系统崩溃会丢失数据。而"rwd","rws",是直接将更新同步到存储设备,系统崩溃时也不会丢失数据。"rws"将内容与元数据的每个更新都同步到存储设备,"rwd"只将内容的更新同步到存储设备。所以一般使用"rwd"。
②byte [] buffer的大小设置大一点,可以加快下载速度。
③当全部线程下载完成后,才能删掉所有的保存位置的文件。
3.断点下载的功能实现代码:
public class DownUtil {
/**
* 下载资源的路径
*/
private String sourcePath;
/**
* 下载资源的文件保存路径
*/
private String filePath;
/**
* 线程的个数
*/
private int threadCount;
/**
* 每个线程下载的区块大小
*/
private long blockSize;
/**
* 正在运行的线程个数
*/
private int runningThreadCount;
public DownUtil(String sourcePath, String filePath, int threadCount) {
this.sourcePath = sourcePath;
this.filePath = filePath;
this.threadCount = threadCount;
}
/**
* 下载
*
* @throws Exception
*/
public void downLoad() throws Exception {
URL url = new URL(sourcePath);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET"); // 发送GET请求
conn.setConnectTimeout(5 * 1000); // 设置连接超时时长
int code = conn.getResponseCode(); // 获得服务器的响应码
if (code == 200) {
int size = conn.getContentLength(); // 得到资源总大小
// 创建一个随机访问的可读写文件
RandomAccessFile file = new RandomAccessFile(filePath, "rw");
file.setLength(size); // 设置文件大小
blockSize = size / threadCount;
runningThreadCount=threadCount;
// 计算每个线程的下载位置
for (int i = 1; i <= threadCount; i++) {
long startIndex = (i - 1) * blockSize; // 线程下载的开始位置
long endIndex = i * blockSize - 1; // 线程下载的结束位置
if (i == threadCount) {
endIndex = size - 1;
}
// 创建并开启线程
new DownThread(startIndex, endIndex, i).start();
}
}
// 释放连接
conn.disconnect();
}
/**
* 线程下载类
*
*/
private class DownThread extends Thread {
/**
* 下载的开始位置
*/
private long startIndex;
/**
* 下载的结束位置
*/
private long endIndex;
/**
* 线程的ID
*/
private int threadId;
public DownThread(long startIndex, long endIndex, int threadId) {
this.startIndex = startIndex;
this.endIndex = endIndex;
this.threadId = threadId;
}
@Override
public void run() {
try {
//已经下载的资源总大小
int total = 0;
//创建一个文件保存下载的位置
File positionFile = new File(
Environment.getExternalStorageDirectory(), threadId
+ ".txt");
//判断该文件是否存在
if (positionFile.exists() && positionFile.length() > 0) {
FileInputStream fis = new FileInputStream(positionFile); // 得到该文件的输入流
BufferedReader br = new BufferedReader(
new InputStreamReader(fis));
int lastTotal = Integer.valueOf(br.readLine()); // 上次下载的总大小
startIndex += lastTotal;
total += lastTotal;
fis.close();
}
URL url = new URL(sourcePath);
HttpURLConnection conn = (HttpURLConnection) url
.openConnection();
conn.setRequestMethod("GET"); // 发送GET请求
// 必须设置该请求参数
conn.setRequestProperty("Range", "bytes=" + startIndex + "-"
+ endIndex);
conn.setConnectTimeout(5 * 1000); // 设置连接超时时长
int code = conn.getResponseCode(); // 获得服务器的响应码
if (code == 206) {
InputStream is = conn.getInputStream(); // 得到输入流
RandomAccessFile raf = new RandomAccessFile(filePath, "rw");
// 从指定位置开始下载
raf.seek(startIndex);
int len = 0;
// 缓存区设置越大,下载速度越快
byte[] buffer = new byte[1024];
while ((len = is.read(buffer)) != -1) {
raf.write(buffer, 0, len);
//创建一个同步更新到存储设备的RandomAccessFile对象
RandomAccessFile file = new RandomAccessFile(
positionFile, "rwd");
total+=len;
//必须将total转化为字节
file.write(String.valueOf(total).getBytes());
file.close();
}
raf.close();
is.close();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println("线程" + threadId + "下载完毕了!");
//当全部线程执行完毕后,才能删除记录位置的文件,所有使用synchronized。
synchronized(DownUtil.class){
runningThreadCount--;
if(runningThreadCount<1){
for(int i=1;i<=threadCount;i++){
File file =new File(Environment.getExternalStorageDirectory(),i+".txt");
//删除成功,返回true
file.delete();
}
}
}
}
}
}
}