package com.hlj.download;
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.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
/**
* java多线程下载
* 1 获得要下载的文件的大小
* 2 用RandomAccessFile生成一个同等大小的文件,相当于一个容器
* 3 计算每个线程下载的开始位置和结束位置
* 4 用conn.setRequestProperty("Range", "bytes=" + start + "-" + end);获得要下载文件的部分资源
* 5 通过RandomAccessFile 的 seek(start)方法设置该线程对该容器写的位置
* @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);// 判断是否全部线程执行完
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 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 ) {
return false;
}
int len = getContentLength();
if (len != -1) {
setFileSize(name, len);// 创建文件
int blockSize = len / threadNum;
long begin = System.currentTimeMillis();
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;
}
return true;
}
/**
* 暂停下载
*/
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) {
String urlPath = conn.getURL().getFile();
String fileFullName = urlPath.substring(urlPath.lastIndexOf("/") + 1);
if (name == null || "".equals(name)) {
name = fileFullName;
}
contentLength = conn.getContentLength();
}
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (ProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return contentLength;
}
/**
* 下载的线程
*
* @author Administrator
*
*/
class MyThread extends Thread {
private int threadId;
private int start;
private int end;
private String name;
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[1024];
while ((len = is.read(buffer)) != -1) {
if (pause) {
synchronized (this) {
wait();
}
}
raf.write(buffer, 0, len);
downProgress.addAndGet(len);
}
is.close();
raf.close();
System.out.println("线程:" + threadId + "下载完毕");
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();
}
}
}
}
测试
<pre name="code" class="java">public static void main(String[] args) throws IOException, InterruptedException {
ThreadLoad down = new ThreadLoad("http://localhost:8080/downLoad/down/qwe.mp4",null, 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();
}
}