原理
1.原理
其实很简单,无非是先获取要下的文件大小,然后在磁盘新建一个同样大小的文件,最后开几个线程分别下载文件的不同部分往磁盘写入。:p
2。实现
这只是一个简单的demo版本,存放下载信息的载体可以视情况使用配置文件和数据库(如:Android可以使用自带的sqlite)
一些说明:
- 1.我为了模拟断点续存用了volatile 标志,每个线程下载4kb*1000后会自动停止并退出,这时候你可打开MP4文件发现后半部分是看不了的,因为还没下载 lol
- 2.我下载的文件是我自己服务器上的,改下url就能下载了
- 3.文件名的获取有些粗暴,这个有时间可以说下怎么解析比较好
- 4.如果是大批多文件下载,可以建个线程池,扔里面下载,这样效率比较高
package servlet;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Properties;
/**
* Created by Coder on 2017/1/19.
*/
public class FileDownload {
//线程同步变量 退出线程的标志
public volatile boolean isExited = false;
//文件的下载地址
private String fileUrl;
//文件的存放路径
private String filePath;
//要下载的文件名
private String filename;
//文件的长度
private long length;
//下载文件的线程的数量
private static final int threadCount = 4;
//配置文件
private Properties properties;
//文件
private File downloadFile;
private File propertiesFile;
public FileDownload(String url, String filePath){
this.fileUrl = url;
this.filePath = filePath;
properties = new Properties();
}
//对下载工作进行初始化
public void init(){
HttpURLConnection conn = null;
RandomAccessFile raf = null;
try {
//获取网络文件流
URL url = new URL(fileUrl);
conn = (HttpURLConnection)url.openConnection();
conn.setConnectTimeout(3000);
conn.setRequestMethod("GET");
// if(conn.getResponseCode() == HttpStatus.sC_OK)
//获取文件长度
length = conn.getContentLength();
//获取文件名,这里是截取URL的最后一个"\"的后面
String str = conn.getURL().getFile();
filename = str.substring(str.lastIndexOf('/')+1);
if(length <= 0){
return;
}
//创建本地文件路径
File dir = new File(filePath);
if(! dir.exists()){
dir.mkdir();
}
downloadFile = new File(dir, filename);
//初始化文件
if(!downloadFile.exists() || downloadFile.length() != length){
//将文件设置为要下载文件的大小
raf = new RandomAccessFile(downloadFile, "rwd");
raf.setLength(length);
}
propertiesFile = new File(dir, filename.substring(0, filename.lastIndexOf('.'))+".properties");
//初始化配置文件
if(!propertiesFile.exists() ){
propertiesFile.createNewFile();
}
//第一次初始化配置文件时,往配置文件里写入文件信息
if(propertiesFile.length() == 0){
properties.setProperty("fileName", filename);
properties.setProperty("fileUrl", fileUrl);
properties.setProperty("filePath", filePath);
properties.setProperty("fileLength", ""+length);
for(int i=0; i<threadCount; i++){
if(i != threadCount-1){
properties.setProperty("thread"+i+"_start", ""+i*(length/threadCount));
properties.setProperty("thread"+i+"_end", ""+(i+1)*(length/threadCount));
}else{
properties.setProperty("thread"+i+"_start", ""+i*(length/threadCount));
properties.setProperty("thread"+i+"_end", ""+length);
}
}
FileOutputStream out = new FileOutputStream(propertiesFile);
properties.store(out, "init my propertiesFile!!!");
}
//载入配置文件
properties = new Properties();
properties.load(new FileInputStream(propertiesFile));
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
if(raf != null){
raf.close();
}
if(conn != null){
conn.disconnect();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public void download(){
for(int i=0; i<threadCount; i++){
System.out.println(properties.toString());
System.out.println("thread"+i+"_start");
long start = Long.parseLong(properties.getProperty("thread"+i+"_start"));
long end = Long.parseLong(properties.getProperty("thread"+i+"_end"));
if(i != threadCount-1){
new DownloadThread("thread"+i, start, end).start();
}else{
new DownloadThread("thread"+i, start, end).start();
}
System.out.println("thread"+i+"已经启动!!!!!");
}
}
public void pause(){
isExited = !isExited;
}
class DownloadThread extends Thread{
private String threadName;
private long start;
private long end;
public DownloadThread(String threadName, long start, long end){
this.threadName = threadName;
this.start = start;
this.end = end;
System.out.println(threadName+" start: "+start+" end: "+end);
}
@Override
public void run() {
HttpURLConnection conn = null;
BufferedInputStream in = null;
FileOutputStream out = null;
RandomAccessFile raf = null;
try {
//获取网络文件流
URL url = new URL(fileUrl);
conn = (HttpURLConnection)url.openConnection();
conn.setConnectTimeout(3000);
conn.setRequestMethod("GET");
in = new BufferedInputStream(conn.getInputStream());
in.skip(start);
raf = new RandomAccessFile(downloadFile, "rwd");
raf.seek(start);
byte[] buf = new byte[1024 * 4];
int len = -1;
int count = 0;
while ((len = in.read(buf)) != -1 && !isExited) {
if(start+len < end){//还未下载完成
raf.write(buf, 0, len);
start += len;
properties.setProperty(threadName+"_start", ""+start);
properties.setProperty(threadName+"_end", ""+end);
}else{//此线程要下载的已经下载完成
raf.write(buf, 0, (int)(end-start));
start = end;
properties.setProperty(threadName+"_start", ""+start);
properties.setProperty(threadName+"_end", ""+end);
break;
}
System.out.println(threadName+" start:"+start+" end:"+end);
count++;
if(count == 1000){
pause();
}
}
//将配置文件保存
out = new FileOutputStream(propertiesFile);
properties.store(out, "download my file !!");
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
if(raf != null){
raf.close();
}
if(in != null){
in.close();
}
if(out != null){
out.flush();
out.close();
}
if(conn != null){
conn.disconnect();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
FileDownload download = new FileDownload("http://192.168.1.107:8080/news/video/65.mp4", "D:/video/");
download.init();
download.download();
}
}