提示:多线程环境下不能使用JUNIT进行测试,除非你能很好的控制,因为这个问题,浪费了很长时间。
/**
* 多线程断点下载工具类
* @author Shmily
*/
public class FileDownloadUtils {
private static Logger logger = Logger.getLogger(FileDownloadUtils.class);
/**
* 多线程文件下载
* @param path 文件下载路径 eg:/mnt/*.txt
* @param storePath 文件的存储路径 eg:/mnt/sdcard/
* @param threadNumber 开启的线程数
* @return 是否下载成功
*/
public static boolean FileDownload(String path,String storePath,Integer threadNumber){
//如果threadNumber为NULL,则默认为三个线程下载文件
int localThreadNumber=(threadNumber==null)?3:threadNumber.intValue();
URL downloadUrl=null;
int fileSize=0;//下载的文件的大小
int fileBlock=0;//每个线程下载的文件块大小
try {
downloadUrl=new URL(path);
//打开链接
HttpURLConnection coon= (HttpURLConnection)downloadUrl.openConnection();
coon.setRequestMethod("GET");
//请求最长延时时间60s
coon.setReadTimeout(60*1000);
int status=coon.getResponseCode();
if(status==200){
logger.info("网络响应成功");
//获得网络响应的文件的大小
fileSize=coon.getContentLength();
//计算每个线程下载的文件块的大小
fileBlock=fileSize/localThreadNumber;
}else{
logger.error("网络响应失败");
}
} catch (Exception e1) {
logger.error("网络地址不存在");
}
RandomAccessFile tempFile=null;//在本地存储的文件
try {
File storeFile=new File(storePath+path.substring(path.lastIndexOf("/")+1));
tempFile=new RandomAccessFile(storeFile, "rwd");
tempFile.setLength(fileSize);//设置文件大小
tempFile.close();
//计算每个线程的起始下载位置
for(int i=0;i<threadNumber;i++){
int start=i*fileBlock;
int end=0;
if(i==(threadNumber-1)){
end=fileSize;
}else{
end=(i+1)*fileBlock-1;
}
//创建新线程
DownloadThread tempThread=new DownloadThread(i, start, end, path,storeFile);
tempThread.start();
logger.info("线程"+i+"开始下载文件,开始位置:"+start+"结束位置:"+end);
}
} catch (Exception e) {
logger.error("创建本地存储文件失败");
}
return true;
}
}
/**
* 下载线程类
* @author Shmily
*/
class DownloadThread extends Thread{
private static Logger logger = Logger.getLogger(DownloadThread.class);
private int threadId;//线程ID
private int start;//线程下载文件的数据起始位置
private int end;//线程下载文件的数据结束位置
private String path;//欲下载的文件的存储路径
private File storeFile;//文件的存储路径
public DownloadThread(int threadId, int start, int end,String path, File storeFile) {
this.threadId = threadId;
this.start = start;
this.end = end;
this.path = path;
this.storeFile=storeFile;
}
@Override
public void run() {
Properties pro=StreamUtils.loadProperty("/com/zgs/stream/util/config.properties");
//该线程下载的文件未下载完毕
if(pro!=null&&pro.getProperty(threadId+"")!=null){
logger.info("线程"+threadId+"从"+(String)pro.getProperty(threadId+"")+"继续下载");
String val=(String)pro.getProperty(threadId+"");
if(Integer.parseInt(val)>start)
//更新下载起始位置
start=Integer.parseInt(val);
}
try {
URL downloadUrl = new URL(path);
//打开链接
HttpURLConnection coon= (HttpURLConnection)downloadUrl.openConnection();
coon.setRequestMethod("GET");
//请求最长延时时间5s
coon.setReadTimeout(5000);
coon.setRequestProperty("Range", "bytes="+start+"-"+ end);
int status=coon.getResponseCode();
logger.info("状态码:"+status);
//多线程断点下载时服务器的响应的状态吗为206
if(status==206||status==200){
//获得存储在本地的文件
RandomAccessFile tempFile=new RandomAccessFile(storeFile, "rwd");
//定位写文件的起始位置
tempFile.seek(start);
//打开服务器响应
InputStream in=coon.getInputStream();
//缓冲区
byte[] buffer=new byte[1024];
int len=0;
int currentLocation=start;//当前读取的文件位置
//读写数据
while((len=in.read(buffer))!=-1){
logger.info("线程"+threadId+"正在读取"+currentLocation);
tempFile.write(buffer,0,len);
currentLocation+=len;
//记录文件下一次开始下载的位置
StreamUtils.updateProperty("/com/zgs/stream/util/config.properties", threadId+"",currentLocation+"");
}
//移除对应的key
StreamUtils.updateProperty("/com/zgs/stream/util/config.properties", threadId+"");
tempFile.close();
logger.info("线程"+threadId+"下载完毕");
}
} catch (Exception e) {
logger.error("网络连接异常");
}
}
}