package com.hlj.download;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
/**
* java多线程断点下载
* 1 读取断点文件 判断是否需要断点下载
* 2 如果不需要就是多线程下载,若需要就读取文件的内容
* 3 获得每个线程下载的开始位置和结束位置
* 4 用conn.setRequestProperty("Range", "bytes=" + start + "-" + end);获得要下载文件的部分资源
* 5 通过RandomAccessFile 的 seek(start)方法设置该线程对该容器写的位置
* 6 下载的时候每个线程将当前的下载位置记录到文件上
* @author wizard
*
*/
public class ThreadLoad {
private String path = null;
private int threadNum = 1;
private boolean pause = false;
private String name = null;
private MyThread thread[];
private int contentLength = -1;
private AtomicInteger count = new AtomicInteger(0);// 判断是否全部线程执行完
private ConcurrentHashMap<String,String> breakPointMap = new ConcurrentHashMap<String,String>();
private List<String> list = new ArrayList<String>();
private BreakPoint breakPoint = null;
public AtomicLong downProgress = new AtomicLong(0);// 判断是否全部线程执行完
public ThreadLoad(String path, String name, int threadNum) {
this.path = path;
this.name = name;
this.threadNum = threadNum;
thread = new MyThread[threadNum + 1];
}
public ThreadLoad(int threadNum) {
this.threadNum = threadNum;
thread = new MyThread[threadNum + 1];
}
public ThreadLoad() {
thread = new MyThread[threadNum + 1];
}
public ConcurrentHashMap<String, String> getBreakPointMap() {
return breakPointMap;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getThreadNum() {
return threadNum;
}
public boolean downLoadFile(String path, String name) throws IOException {
setPath(path);
setName(name);
return downLoadFile();
}
/**
* 判断是否全部结束额
* @return
*/
public boolean finish(){
return count.get() == threadNum;
}
/**
* 下载
*
* @return
* @throws IOException
*/
public boolean downLoadFile() throws IOException {
if ("".equals(path) || path == null || "".equals(name) || name == null ) {
return false;
}
if(readBreakPointFile()){
System.out.println("断点。。。。。。。。。。。。。。。。");
thread = new MyThread[list.size() + 1];
threadNum = list.size();
for (int threadId = 1; threadId <= list.size(); threadId++) {
String sz[] = list.get(threadId-1).split(";");
System.out.println(list.get(threadId-1));
System.out.println("线程:" + threadId + "下载:---" + sz[0] + "--->" + sz[1]);
thread[threadId] = new MyThread(threadId, Integer.valueOf(sz[0]), Integer.valueOf(sz[1]), name);
thread[threadId].start();
}
}else{
int len = getContentLength();
System.out.println("非断点");
if (len != -1) {
setFileSize(name, len);// 创建文件
int blockSize = len / threadNum;
for (int threadId = 1; threadId <= threadNum; threadId++) {
int start = (threadId - 1) * blockSize;
int endIndex = threadId * blockSize - 1;
if (threadId == threadNum) {// 最后一个线程下载的长度要稍微长一点
endIndex = len;
}
System.out.println("线程:" + threadId + "下载:---" + start + "--->" + endIndex);
thread[threadId] = new MyThread(threadId, start, endIndex, name);
thread[threadId].start();
}
} else {
return false;
}
}
breakPoint = new BreakPoint(this);//创建断点下载的线程
breakPoint.start();
return true;
}
/**
* 判断是否暂停了
* @return
*/
public boolean isPause(){
return pause;
}
/**
* 暂停下载
*/
public void pauseDown() {
pause = true;
}
/**
* 继续下载
*/
public void contiuneDown() {
pause = false;
for (int i = 1; i <= threadNum; i++) {
if (thread[i] != null) {
synchronized (thread[i]) {
thread[i].notify();
}
}
}
}
/**
* 先设置一个空的文件 但长度要和要下载的一样大 相当于一个容器
*
* @param name
* @param len
* @throws IOException
*/
public void setFileSize(String name, int len) throws IOException {
RandomAccessFile raf = new RandomAccessFile(name, "rw");
raf.setLength(len);
raf.close();
}
/**
* 得到文件的总长度
*
* @return
* @throws IOException
*/
public int getContentLength() {
try {
if (contentLength == -1) {
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5000);
conn.setRequestMethod("GET");
int code = conn.getResponseCode();
if (code == 200) {
contentLength = conn.getContentLength();
}
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (ProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return contentLength;
}
/**
* 下载之前读断点文件 false就是要重新下载
* @throws IOException
* @throws FileNotFoundException
*/
public boolean readBreakPointFile() throws FileNotFoundException, IOException{
File file = new File(name);
if(file.exists()){
Properties properties = new Properties();
properties.load(new FileInputStream("download.properties"));
if(properties.containsKey("name") && properties.getProperty("path").equals(path)){
Set<Object> keySet = properties.keySet();
Iterator<Object> it = keySet.iterator();
contentLength = Integer.valueOf(properties.getProperty("fileSize"));
while(it.hasNext()){
String key = (String) it.next();
if("downLoadSize".equals(key)){
downProgress.set(Long.valueOf(properties.getProperty(key)));
}else if(!"name".equals(key) && !"path".equals(key)&& !"fileSize".equals(key)){
list.add(properties.getProperty(key));
}
}
}else{
return false;
}
}else{
return false;
}
return true;
}
/**
* 下载的线程
*
* @author Administrator
*
*/
class MyThread extends Thread {
private int threadId;
private int start;
private int end;
private String name;
private int nowDownSize = 0;
public MyThread(int threadId, int start, int end, String name) {
super();
this.threadId = threadId;
this.start = start;
this.end = end;
this.name = name;
}
@Override
public void run() {
try {
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5000);
conn.setRequestMethod("GET");
// 重要:请求服务器下载部分文件 指定文件的位置
conn.setRequestProperty("Range", "bytes=" + start + "-" + end);
// 从服务器请求全部资源返回200 如果从服务器请求部分资源 返回 206
int code = conn.getResponseCode();
InputStream is = conn.getInputStream();// 已经设置了请求的位置,返回的是当前位置对应的文件的输入流
RandomAccessFile raf = new RandomAccessFile(name, "rwd");
raf.seek(start);
int len = 0;
byte[] buffer = new byte[10];
while ((len = is.read(buffer)) != -1) {
if (pause) {
synchronized (this) {
wait();
}
}
raf.write(buffer, 0, len);
nowDownSize+=len;
breakPointMap.put(threadId+"", start+nowDownSize+";"+end);
downProgress.addAndGet(len);
breakPointMap.put("downLoadSize",downProgress.get()+"");
}
is.close();
raf.close();
System.out.println("线程:" + threadId + "下载完毕");
breakPointMap.put(threadId+"", start+nowDownSize+";"+end);
count.incrementAndGet();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (ProtocolException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
package com.hlj.download;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Iterator;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* 用于保存下载信息 每秒钟保存一次每个下载线程的下载位置
* @author wizard
*
*/
public class BreakPoint extends Thread {
private Properties properties = new Properties();
private String name = null;
private String path = null;
private String fileSize = null;
private ConcurrentHashMap<String, String> breakPointMap = null;
private FileOutputStream oFile = null;
private ThreadLoad thradLoad = null;
public BreakPoint(ThreadLoad thradLoad) throws IOException {
this.thradLoad = thradLoad;
name = thradLoad.getName();
path = thradLoad.getPath();
fileSize = thradLoad.getContentLength()+"";
breakPointMap = thradLoad.getBreakPointMap();
oFile = new FileOutputStream("download.properties");
saveNameAndPathAndFileSize(name, path,fileSize);
oFile.close();
}
/**
* 下载完成后删除
*
* @throws IOException
*/
public void clear() throws IOException {
oFile = new FileOutputStream("download.properties");
properties.clear();
properties.store(oFile, "Comment");
}
/**
* 保存名字和path
*
* @param name
* @param path
* @throws IOException
*/
public void saveNameAndPathAndFileSize(String name, String path,String fileSize) throws IOException {
properties.setProperty("name", name);
properties.setProperty("path", path);
properties.setProperty("fileSize", fileSize);
properties.store(oFile, "Comment");
}
@Override
public void run() {
try {
while (!thradLoad.finish()) {// 判断下载完成没
if (!thradLoad.isPause()) {// 判断暂停没
saveBreakPoint();
}
Thread.sleep(100);
}
clear();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 保存断点信息
*
* @throws IOException
*/
private void saveBreakPoint() throws IOException {
Set<String> keySet = breakPointMap.keySet();
Iterator<String> it = keySet.iterator();
oFile = new FileOutputStream("download.properties");
while (it.hasNext()) {
String key = it.next();
String vaule = breakPointMap.get(key);
String sz[] = vaule.split(";");
if(!"downLoadSize".equals(key)&&Integer.valueOf(sz[0])>=Integer.valueOf(sz[1])){
properties.remove(key);
}else{
properties.setProperty(key, vaule);
}
}
try {
properties.store(oFile, "Comment");
} catch (IOException e) {
e.printStackTrace();
}
oFile.close();
}
}
public static void main(String[] args) throws IOException, InterruptedException {//测试
ThreadLoad down = new ThreadLoad("http://localhost:8080/downLoad/down/3.jpg","3.jpg", 5);
down.downLoadFile();
long preLen = 0;
int len = down.getContentLength();
while (!down.finish()) {
Thread.sleep(1000);
long now = down.downProgress.get();
float speed = ((float)(now - preLen) / 1024);
if(speed==0.0){
speed=1f;
}
float precent = (((float) now) / len) * 100;
float total = (float)len/1024/1024;
float surplusTime = ((len-now)/speed)/1000;
System.out.println("已下载: " + now + "---" + precent + "%" + " 下载速度:"
+ speed + "k/"+total+"M 剩余"+surplusTime+"秒");
preLen = down.downProgress.get();
}
}