package com.hoo.download;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import com.hoo.entity.DownloadInfo;
import com.hoo.util.LogUtils;
/**
* <b>function:</b> 分批量下载文件
* @author hoojo
* @createDate 2011-9-22 下午05:51:54
* @file BatchDownloadFile.java
* @package com.hoo.download
* @project MultiThreadDownLoad
* @blog http://blog.csdn.net/IBM_hoojo
* @email hoojo_@126.com
* @version 1.0
*/
public class BatchDownloadFile implements Runnable { //下载文件信息
private DownloadInfo downloadInfo;
//一组开始下载位置
private long[] startPos;
//一组结束下载位置
private long[] endPos;
//休眠时间
private static final int SLEEP_SECONDS = 500;
//子线程下载
private DownloadFile[] fileItem;
//文件长度
private int length;
//是否第一个文件
private boolean first = true;
//是否停止下载
private boolean stop = false;
//临时文件信息
private File tempFile;
public BatchDownloadFile(DownloadInfo downloadInfo) { this.downloadInfo = downloadInfo;
String tempPath = this.downloadInfo.getFilePath() + File.separator + downloadInfo.getFileName() + ".position";
tempFile = new File(tempPath);
//如果存在读入点位置的文件
if (tempFile.exists()) { first = false;
//就直接读取内容
try { readPosInfo();
} catch (IOException e) { e.printStackTrace();
}
} else { //数组的长度就要分成多少段的数量
startPos = new long[downloadInfo.getSplitter()];
endPos = new long[downloadInfo.getSplitter()];
}
}
@Override
public void run() { //首次下载,获取下载文件长度
if (first) { length = this.getFileSize();//获取文件长度
if (length == -1) { LogUtils.log("file length is know!"); stop = true;
} else if (length == -2) { LogUtils.log("read file length is error!"); stop = true;
} else if (length > 0) { /**
* eg
* start: 1, 3, 5, 7, 9
* end: 3, 5, 7, 9, length
*/
for (int i = 0, len = startPos.length; i < len; i++) { int size = i * (length / len);
startPos[i] = size;
//设置最后一个结束点的位置
if (i == len - 1) { endPos[i] = length;
} else { size = (i + 1) * (length / len);
endPos[i] = size;
}
LogUtils.log("start-end Position[" + i + "]: " + startPos[i] + "-" + endPos[i]); }
} else { LogUtils.log("get file length is error, download is stop!"); stop = true;
}
}
//子线程开始下载
if (!stop) { //创建单线程下载对象数组
fileItem = new DownloadFile[startPos.length];//startPos.length = downloadInfo.getSplitter()
for (int i = 0; i < startPos.length; i++) { try { //创建指定个数单线程下载对象,每个线程独立完成指定块内容的下载
fileItem[i] = new DownloadFile(
downloadInfo.getUrl(),
this.downloadInfo.getFilePath() + File.separator + downloadInfo.getFileName(),
startPos[i], endPos[i], i
);
fileItem[i].start();//启动线程,开始下载
LogUtils.log("Thread: " + i + ", startPos: " + startPos[i] + ", endPos: " + endPos[i]); } catch (IOException e) { e.printStackTrace();
}
}
//循环写入下载文件长度信息
while (!stop) { try { writePosInfo();
LogUtils.log("downloading……"); Thread.sleep(SLEEP_SECONDS);
stop = true;
} catch (IOException e) { e.printStackTrace();
} catch (InterruptedException e) { e.printStackTrace();
}
for (int i = 0; i < startPos.length; i++) { if (!fileItem[i].isDownloadOver()) { stop = false;
break;
}
}
}
LogUtils.info("Download task is finished!"); }
}
/**
* 将写入点数据保存在临时文件中
* @author hoojo
* @createDate 2011-9-23 下午05:25:37
* @throws IOException
*/
private void writePosInfo() throws IOException { DataOutputStream dos = new DataOutputStream(new FileOutputStream(tempFile));
dos.writeInt(startPos.length);
for (int i = 0; i < startPos.length; i++) { dos.writeLong(fileItem[i].getStartPos());
dos.writeLong(fileItem[i].getEndPos());
//LogUtils.info("[" + fileItem[i].getStartPos() + "#" + fileItem[i].getEndPos() + "]"); }
dos.close();
}
/**
* <b>function:</b>读取写入点的位置信息
* @author hoojo
* @createDate 2011-9-23 下午05:30:29
* @throws IOException
*/
private void readPosInfo() throws IOException { DataInputStream dis = new DataInputStream(new FileInputStream(tempFile));
int startPosLength = dis.readInt();
startPos = new long[startPosLength];
endPos = new long[startPosLength];
for (int i = 0; i < startPosLength; i++) { startPos[i] = dis.readLong();
endPos[i] = dis.readLong();
}
dis.close();
}
/**
* <b>function:</b> 获取下载文件的长度
* @author hoojo
* @createDate 2011-9-26 下午12:15:08
* @return
*/
private int getFileSize() { int fileLength = -1;
try { URL url = new URL(this.downloadInfo.getUrl());
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
DownloadFile.setHeader(conn);
int stateCode = conn.getResponseCode();
//判断http status是否为HTTP/1.1 206 Partial Content或者200 OK
if (stateCode != HttpURLConnection.HTTP_OK && stateCode != HttpURLConnection.HTTP_PARTIAL) { LogUtils.log("Error Code: " + stateCode); return -2;
} else if (stateCode >= 400) { LogUtils.log("Error Code: " + stateCode); return -2;
} else { //获取长度
fileLength = conn.getContentLength();
LogUtils.log("FileLength: " + fileLength); }
//读取文件长度
/*for (int i = 1; ; i++) { String header = conn.getHeaderFieldKey(i);
if (header != null) { if ("Content-Length".equals(header)) { fileLength = Integer.parseInt(conn.getHeaderField(i));
break;
}
} else { break;
}
}
*/
DownloadFile.printHeader(conn);
} catch (MalformedURLException e) { e.printStackTrace();
} catch (IOException e) { e.printStackTrace();
}
return fileLength;
}
}