package mydown;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
/**
* 多线程断点续传
*
* @author kinkding
* @history 2009-7-25
*/
public class TestMyDown {
int threads;
String rootPath = "F:/mydown";
String fileName = "";
URL downUrl;
CountDownLatch latch;
public TestMyDown(String strUrl, int threads) throws MalformedURLException {
downUrl = new URL(strUrl);
this.threads = threads;
fileName = strUrl.substring(strUrl.lastIndexOf('/') + 1);
latch = new CountDownLatch(threads);
}
public int run() throws IOException {
// 创建根路径
File rootFile = new File(rootPath);
if (!rootFile.exists()) {
rootFile.mkdirs();
}
HttpURLConnection urlConn = (HttpURLConnection) downUrl.openConnection();
this.setHeader(urlConn);
long totalLength = urlConn.getContentLength();
long threadLength = totalLength / threads;
// 设置断点
long startPos[] = this.setBreakPoint(totalLength);
// 开始进行多线程下载
DownThread downThreads[] = new DownThread[threads];
ExecutorService exec = Executors.newCachedThreadPool();
for (int i = 0; i < threads; i++) {
startPos[i] += threadLength * i;
long endPos = i == threads - 1 ? totalLength : (threadLength * (i + 1) - 1);
// 开启子线程并执行
DownThread child = new DownThread(startPos[i], endPos, i);
downThreads[i] = child;
exec.execute(child);
}
try {
// 等待所有线程下载完成
latch.await();
exec.shutdown();
// 开始合并文件
this.mergeFile(downThreads);
} catch (InterruptedException e) {
e.printStackTrace();
}
return 1;
}
private long[] setBreakPoint(long totalLength) throws IOException {
long startPos[] = new long[threads];
File file = new File(this.rootPath + '/' + this.fileName);
if (file.exists() && file.length() < totalLength) {
File root = new File(this.rootPath);
for (File f : root.listFiles()) {
String name = f.getName();
long length = f.length();
if (name != null && length > 0 && name.startsWith(this.fileName + '_')) {
startPos[Integer.parseInt(name.substring(name.lastIndexOf('_') + 1))] = length;
}
}
} else {
file.createNewFile();
}
return startPos;
}
private void mergeFile(DownThread downThreads[]) throws IOException {
BufferedOutputStream output = new BufferedOutputStream(new FileOutputStream(this.rootPath
+ "/" + this.fileName));
for (int i = 0; i < threads; i++) {
BufferedInputStream input = new BufferedInputStream(new FileInputStream(
downThreads[i].file));
int len = 0;
long count = 0;
byte[] b = new byte[1024];
while ((len = input.read(b)) != -1) {
count += len;
output.write(b, 0, len);
if ((count % 4096) == 0) {
output.flush();
}
}
input.close();
downThreads[i].file.delete();
}
output.flush();
output.close();
System.out.println(fileName + " ok!");
}
private void setHeader(HttpURLConnection urlConn) {
// 模拟浏览器的访问
urlConn
.setRequestProperty("User-Agent",
"Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.1.1) Gecko/20090715 Firefox/3.5.1");
urlConn.setRequestProperty("Accept-Language", "zh-cn,zh;q=0.5");
urlConn.setRequestProperty("Accept-Charset", "GB2312,utf-8;q=0.7,*;q=0.7");
urlConn.setRequestProperty("Keep-Alive", "300");
urlConn.setRequestProperty("Connection", "keep-alive");
urlConn.setRequestProperty("Cache-Control", "max-age=0");
urlConn.setRequestProperty("Referer", downUrl.getPath());
}
class DownThread extends Thread {
private File file;
private int id;
private long startPos;
private long endPos;
public DownThread(long startPos, long endPos, int id) {
super();
this.startPos = startPos;
this.endPos = endPos;
this.id = id;
file = new File(rootPath + "/" + fileName + "_" + id);
if (!file.exists()) {
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void run() {
System.out.println("thread " + id + " start...");
HttpURLConnection con = null;
InputStream inputStream = null;
BufferedOutputStream outputStream = null;
long count = 0;
try {
outputStream = new BufferedOutputStream(new FileOutputStream(file.getPath(), true));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
while (true) {
try {
con = (HttpURLConnection) downUrl.openConnection();
setHeader(con);
con.setAllowUserInteraction(true);
con.setConnectTimeout(1000);
con.setReadTimeout(1000);
if (startPos < endPos) {
// 设置下载数据的起止区间
System.out.println("thread" + id + " bytes=" + startPos + "-" + endPos);
con.setRequestProperty("Range", "bytes=" + startPos + "-" + endPos);
int code = con.getResponseCode();
// 服务器是否响应成功
if (code != HttpURLConnection.HTTP_OK
&& code != HttpURLConnection.HTTP_PARTIAL) {
outputStream.close();
con.disconnect();
latch.countDown();
break;
}
inputStream = con.getInputStream();
int len = 0;
byte[] b = new byte[1024];
while ((len = inputStream.read(b)) != -1) {
outputStream.write(b, 0, len);
count += len;
startPos += len;
if (count % 4096 == 0) {
outputStream.flush();
}
}
outputStream.flush();
outputStream.close();
inputStream.close();
con.disconnect();
}
System.out.println("thread " + id + " end.");
latch.countDown();
break;
} catch (IOException e) {
try {
outputStream.flush();
inputStream.close();
con.disconnect();
// 2秒后重连
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e1) {
e1.printStackTrace();
} catch (IOException e2) {
e2.printStackTrace();
}
continue;
}
}
// 循环终止
if (outputStream != null) {
try {
outputStream.close();
con.disconnect();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
String url = "http://i1.sinaimg.cn/dy/hdphoto/2009/0425/U1565P1T557D132F12892DT20090425105719.jpg";
try {
TestMyDown mydown = new TestMyDown(url, 4);
mydown.run();
} catch (IOException e) {
e.printStackTrace();
}
}
}
程序运行结果如下:
thread 0 start...
thread0 bytes=0-46078
thread 1 start...
thread1 bytes=46079-92157
thread 2 start...
thread2 bytes=92158-138236
thread 3 start...
thread3 bytes=138237-184319
thread2 bytes=92158-138236
thread 0 end.
thread1 bytes=74995-92157
thread 1 end.
thread3 bytes=174453-184319
thread 3 end.
thread2 bytes=114648-138236
thread 2 end.
U1565P1T557D132F12892DT20090425105719.jpg ok!