java 断点续传 多线程

来源:互联网 发布:波士顿矩阵分析法应用 编辑:程序博客网 时间:2024/06/05 16:59
由http://www.blogjava.net/bitmap/archive/2009/07/20/287300.html整理而来

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;


public class RunnableHttpURL 
{
static String urlStr = "http://www.greenxiazai.com/soft/UploadPic/2012-1/2012178304237718.jpg";
static String charset = "utf-8";
String fileName = urlStr.substring(urlStr.lastIndexOf("/")+1);//获得要下载的文件的文件名;
String fileDir = "d:\\a\\";  //下载的文件存放到这个目录下;
int threadNum = 3; //分三个子线程下载;
ChildRunnable[] childRunnable = new ChildRunnable[threadNum];
public boolean statusError = false;

public static void main(String[] args) 
{
RunnableHttpURL runnableHttpURL = new RunnableHttpURL();
runnableHttpURL.setSleepSeconds(5);
runnableHttpURL.download(urlStr, charset);
}
private void setSleepSeconds(int i) 
{

}
/*
 * urlStr是要下载的页面的url,charset是编码方式;CountDownLatch一个同步辅助类,
 * 在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。
 */
public void download(String urlStr, String charset)
{
HttpURLConnection huc = null;
try 
{
URL url = new URL(urlStr); //要下载的文件的url;
huc = (HttpURLConnection)url.openConnection();
} catch (MalformedURLException e1) 
{
e1.printStackTrace();
} catch (IOException e1) 
{
e1.printStackTrace();
}
CountDownLatch cdl = new CountDownLatch(threadNum); //一个同步辅助类
setHeader(huc);
long[] start = new long[threadNum]; //每个线程开始下载的位置;
long end = 0;    //每个线程结束下载的位置;
long contentLength = huc.getContentLength();              //要下载的文件的大小;
long threadLength = contentLength/threadNum;   //每段线程的长度;
System.out.println(threadLength);
start = setThreadBreakpoint(fileDir, fileName, contentLength, start);//分析已下载的临时文件,设置断点;
ExecutorService es = Executors.newCachedThreadPool();     //分多个线程下载文件;


for(int i=0; i<threadNum; i++)
{
start[i] += threadLength * i;
if(i == threadNum - 1)
{
end = contentLength;
}
else
{
end = threadLength * (i+1)-1;
}
ChildRunnable thread = new ChildRunnable(this, cdl, i, start[i], end); //开启子线程,并执行;
childRunnable[i] = thread;
es.execute(thread);
}
try 
{
cdl.await();   //等待CountdownLatch信号为0,表示所有子线程都结束;
es.shutdown();  //shutdown() 方法在终止前允许执行以前提交的任务;

tempFileToTargeFile(childRunnable);//把分段下载下来的临时文件写入目标文件;
} catch (InterruptedException e) 
{
e.printStackTrace();
}

}
//把分段下载下来的临时文件写入目标文件;
private void tempFileToTargeFile(ChildRunnable[] childRunnables) 
{
try {
BufferedOutputStream tbos = new BufferedOutputStream(
new FileOutputStream(fileDir + fileName));
//遍历所有子线程创建的临时文件,按顺序把下载的内容写入目标文件中;
for(int i=0; i<threadNum; i++)
{
if(statusError)
{
for(int k=0; k<threadNum; k++)
{
if(childRunnable[k].tempFile.length()==0)
{
childRunnable[k].tempFile.delete();
}
}
System.out.println("本次下载任务不成功,请重新设置线程数。");
break;
}
BufferedInputStream bis = new BufferedInputStream(
new FileInputStream(childRunnable[i].tempFile));
System.out.println("Now is file " + childRunnable[i].id);
int len = 0;
int count = 0;
byte[] b = new byte[1024];
while((len = bis.read(b)) != -1)
{
tbos.write(b, 0, len);
count += len;
if(count % 5000 == 0)//每5000个字节刷新一次;
{
tbos.flush();
}
}
bis.close();

// if(childRunnable[i].status == ChildRunnable.STATUS_HAS_FINISHED)
// {
childRunnable[i].tempFile.delete();  //删除临时文件;
// }
}
System.out.println("文件已下载!");
tbos.flush();
tbos.close();
} catch (FileNotFoundException e) 
{
e.printStackTrace();
} catch (IOException e) 
{
e.printStackTrace();
}
    }
//分析已下载的临时文件,设置断点,如果是新的下载任务,则建立目标文件;
private long[] setThreadBreakpoint(String fileDir, String fileName,
long contentLength, long[] start) 
{
File file = new File(fileDir + fileName);
long localFileSize = file.length();
if(file.exists())
{
System.out.println("file " + fileName + " has exists.");
//下载的目标文件已经存在,判断目标文件是否完整;
if(localFileSize < contentLength)
{
System.out.println("Now download continue…………");

//遍历目标文件的所有临时文件,设置断点的位置,即每个临时文件的长度;
File tempFileDir = new File(fileDir);
File[] files = tempFileDir.listFiles();
for(int k=0; k<files.length; k++)
{
String tempFileName = files[k].getName();
//临时文件的命名方式为:目标文件名+"_"+编号;
if(tempFileName != null && files[k].length() > 0
&& tempFileName.startsWith(fileName + "_"))
{
int fileLongNum = Integer.parseInt(tempFileName.
substring(tempFileName.lastIndexOf("_")+1,
tempFileName.lastIndexOf("_")+2));
//为每个线程设置已下载的位置;
start[fileLongNum] = files[k].length();
}
}
}
}
else
{
try 
{
file.createNewFile();//如果下载的目标文件不存在,则创建新文件;
} catch (IOException e) 
{
e.printStackTrace();
}
}
return start;
}
/*
* 通过URLConnection来获得一个http的连接。有些网站为了安全起见,
* 会对请求的http连接进行过滤,因此为了伪装这个http的连接请求,
* 我们给httpHeader穿一件伪装服。下面的setHeader方法展示了一些
* 非常常用的典型的httpHeader的伪装方法。比较重要的有:Uer-Agent
* 模拟从Ubuntu的firefox浏览器发出的请求;Referer模拟浏览器请求
* 的前一个触发页面,例如从skycn站点来下载软件的话,Referer设置
* 成skycn的首页域名就可以了;Range就是这个连接获取的流文件的起始区间。
*/
private void setHeader(HttpURLConnection huc) 
{
huc.setRequestProperty("User-Agent", "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.3) Gecko/2008092510 Ubuntu/8.04 (hardy) Firefox/3.0.3");   
huc.setRequestProperty("Accept-Language", "en-us,en;q=0.7,zh-cn;q=0.3");   
   huc.setRequestProperty("Accept-Encoding", "aa");   
   huc.setRequestProperty("Accept-Charset", "ISO-8859-1,utf-8;q=0.7,*;q=0.7");   
   huc.setRequestProperty("Keep-Alive", "300");   
   huc.setRequestProperty("Connection", "keep-alive");   
   huc.setRequestProperty("If-Modified-Since", "Fri, 02 Jan 2009 17:00:05 GMT");   
   huc.setRequestProperty("If-None-Match", "\"1261d8-4290-df64d224\"");   
   huc.setRequestProperty("Cache-Control", "max-age=0");   
   huc.setRequestProperty("Referer", "http://http://www.bt285.cn");   
}

public class ChildRunnable extends Thread
{


public static final int STATUS_HASNOT_FINISHED = 0;
public static final int STATUS_HAS_FINISHED = 1;
public static final int STATUS_HTTPSTATUS_ERROR = 2;
private int id;     //第id个子线程;
private long start; //子线程开始的位置;
private long end;   //子线程结束的位置;
private File tempFile = null;   //临时文件;
private CountDownLatch cdl;//子线程结束信号计数器;
private int status = ChildRunnable.STATUS_HASNOT_FINISHED;
private RunnableHttpURL rhu;
public ChildRunnable(RunnableHttpURL runnableHttpURL, CountDownLatch cdl, int id, long start, long end)
{
this.cdl = cdl;
this.id = id;
this.start = start;
this.end = end;
this.rhu = runnableHttpURL;
try 
{
tempFile = new File(this.rhu.fileDir + this.rhu.fileName + "_" + id);
if(!tempFile.exists())
{
tempFile.createNewFile();
}
} catch (IOException e) 
{
e.printStackTrace();
}
}
@Override
public void run() 
{
System.out.println(Thread.currentThread().getName() + "run……");
URL url = null;
try 
{
url = new URL(urlStr); //要下载的文件的url;
} catch (MalformedURLException e1) 
{
e1.printStackTrace();
}  
HttpURLConnection huc = null;
InputStream is = null;
BufferedOutputStream bos = null;
int count = 0;
long downloadLength = end - start;
System.out.println("要下载的长度:" + downloadLength + "---------------");
try {
bos = new BufferedOutputStream(new FileOutputStream(tempFile.getPath()));
} catch (FileNotFoundException e) 
{
e.printStackTrace();
}
for(;;)
{
start += count;

try 
{
huc = (HttpURLConnection) url.openConnection();  //打开URLConnection;
setHeader(huc);
huc.setAllowUserInteraction(true);
huc.setConnectTimeout(10000); //设置连接超时时间为10000ms;
huc.setReadTimeout(10000);    //设置超时时间为10000ms;

if(start < end)
{
huc.setRequestProperty("range", "bytes="+start+"-"+end);//设置下载数据的起始区间;
System.out.println("Thread " + id + " start is " + start);
System.out.println("Thread " + id + " end is " + end);
System.out.println("响应状态码:" + huc.getResponseCode());
if(huc.getResponseCode() != HttpURLConnection.HTTP_OK
&& huc.getResponseCode() != HttpURLConnection.HTTP_PARTIAL)
{
System.out.println("Thread " + id + " : code = " + huc.getResponseCode()
+ ", status = " + huc.getResponseMessage());
status = ChildRunnable.STATUS_HTTPSTATUS_ERROR;
this.rhu.statusError = true;
bos.close();
huc.disconnect();
System.out.println("Thread " + id + " finished.");
cdl.countDown();
break;
}
is = huc.getInputStream();
int len = 0;
byte[] b = new byte[1024];
while((len = is.read(b)) != -1)
{
bos.write(b, 0, len);
count += len;
if(count % 5000 == 0)
{
bos.flush();
}
}


System.out.println("count is " + count);
bos.flush();
bos.close();
is.close();
huc.disconnect();

System.out.println("Thread " + id + "finished.");
cdl.countDown();
break;
}
} catch (IOException e) 
{
try {
bos.flush();
TimeUnit.SECONDS.sleep(getSleepSeconds());
} catch (IOException e1) 
{
e1.printStackTrace();
} catch (InterruptedException e1)
{
e1.printStackTrace();
}
e.printStackTrace();
}
continue;
}

}
private long getSleepSeconds() 
{
return 0;
}


}


}
































0 0
原创粉丝点击