使用Apache HttpClient实现多线程下载的小例子

Posted jdsjlzx

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用Apache HttpClient实现多线程下载的小例子相关的知识,希望对你有一定的参考价值。


网上类似的文章很多,参考了很多人的,大部分人都是用URLConnection写的。

原理一:HTTP多线程下载原理

1、发送一个含有Rang头的Head请求,如果返回状态码为206,则允许多线程下载

 

原理二:多线程下载原理

1、使用HttpClient的Head请求获取请求文件的信息

2、发送一个Rang的Head请求判断是否允许多线程下载

3、通过主任务创建多个分段下载线程,分段下载文件,然后用Java的随机读写文件类保存下载的内容

 

等有时间了再添加内容吧,先简单写这么多

调度功能代码片段

Java代码

/**
* 开始下载
* @throws Exception
*/
public void startDown() throws Exception
HttpClient httpClient = new DefaultHttpClient();
try
//获取下载文件信息
getDownloadFileInfo(httpClient);
//启动多个下载线程
startDownloadThread();
//开始监视下载数据
monitor();
catch (Exception e)
throw e;
finally
httpClient.getConnectionManager().shutdown();



/**
* 获取下载文件信息
*/
private void getDownloadFileInfo(HttpClient httpClient) throws IOException,
ClientProtocolException, Exception
HttpHead httpHead = new HttpHead(url);
HttpResponse response = httpClient.execute(httpHead);
//获取HTTP状态码
int statusCode = response.getStatusLine().getStatusCode();

if(statusCode != 200) throw new Exception("资源不存在!");
if(getDebug())
for(Header header : response.getAllHeaders())
System.out.println(header.getName()+":"+header.getValue());



//Content-Length
Header[] headers = response.getHeaders("Content-Length");
if(headers.length > 0)
contentLength = Long.valueOf(headers[0].getValue());

httpHead.abort();

httpHead = new HttpHead(url);
httpHead.addHeader("Range", "bytes=0-"+(contentLength-1));
response = httpClient.execute(httpHead);
if(response.getStatusLine().getStatusCode() == 206)
acceptRanges = true;

httpHead.abort();


/**
* 启动多个下载线程
* @throws IOException
* @throws FileNotFoundException
*/
private void startDownloadThread() throws IOException,
FileNotFoundException
//创建下载文件
File file = new File(localPath);
file.createNewFile();
RandomAccessFile raf = new RandomAccessFile(file, "rw");
raf.setLength(contentLength);
raf.close();

//定义下载线程事件实现类
DownloadThreadListener listener = new DownloadThreadListener()
public void afterPerDown(DownloadThreadEvent event)
//下载完一个片段后追加已下载字节数
synchronized (object)
DownloadTask.this.receivedCount += event.getCount();



public void downCompleted(DownloadThreadEvent event)
//下载线程执行完毕后从主任务中移除
threads.remove(event.getTarget());
if(getDebug())
System.out.println("剩余线程数:"+threads.size());


;

//不支持多线程下载时
if (!acceptRanges)
if(getDebug())
System.out.println("该地址不支持多线程下载");

//定义普通下载
DownloadThread thread = new DownloadThread(url, 0, contentLength, file, false);
thread.addDownloadListener(listener);
thread.start();
threads.add(thread);
return;


//每个请求的大小
long perThreadLength = contentLength / threadCount + 1;
long startPosition = 0;
long endPosition = perThreadLength;
//循环创建多个下载线程
do
if(endPosition >= contentLength)
endPosition = contentLength - 1;

DownloadThread thread = new DownloadThread(url, startPosition, endPosition, file);
thread.addDownloadListener(listener);
thread.start();
threads.add(thread);

startPosition = endPosition + 1;//此处加 1,从结束位置的下一个地方开始请求
endPosition += perThreadLength;
while (startPosition < contentLength);
/**
* 开始下载
* @throws Exception
*/
public void startDown() throws Exception
HttpClient httpClient = new DefaultHttpClient();
try
//获取下载文件信息
getDownloadFileInfo(httpClient);
//启动多个下载线程
startDownloadThread();
//开始监视下载数据
monitor();
catch (Exception e)
throw e;
finally
httpClient.getConnectionManager().shutdown();



/**
* 获取下载文件信息
*/
private void getDownloadFileInfo(HttpClient httpClient) throws IOException,
ClientProtocolException, Exception
HttpHead httpHead = new HttpHead(url);
HttpResponse response = httpClient.execute(httpHead);
//获取HTTP状态码
int statusCode = response.getStatusLine().getStatusCode();

if(statusCode != 200) throw new Exception("资源不存在!");
if(getDebug())
for(Header header : response.getAllHeaders())
System.out.println(header.getName()+":"+header.getValue());



//Content-Length
Header[] headers = response.getHeaders("Content-Length");
if(headers.length > 0)
contentLength = Long.valueOf(headers[0].getValue());

httpHead.abort();

httpHead = new HttpHead(url);
httpHead.addHeader("Range", "bytes=0-"+(contentLength-1));
response = httpClient.execute(httpHead);
if(response.getStatusLine().getStatusCode() == 206)
acceptRanges = true;

httpHead.abort();


/**
* 启动多个下载线程
* @throws IOException
* @throws FileNotFoundException
*/
private void startDownloadThread() throws IOException,
FileNotFoundException
//创建下载文件
File file = new File(localPath);
file.createNewFile();
RandomAccessFile raf = new RandomAccessFile(file, "rw");
raf.setLength(contentLength);
raf.close();

//定义下载线程事件实现类
DownloadThreadListener listener = new DownloadThreadListener()
public void afterPerDown(DownloadThreadEvent event)
//下载完一个片段后追加已下载字节数
synchronized (object)
DownloadTask.this.receivedCount += event.getCount();



public void downCompleted(DownloadThreadEvent event)
//下载线程执行完毕后从主任务中移除
threads.remove(event.getTarget());
if(getDebug())
System.out.println("剩余线程数:"+threads.size());


;

//不支持多线程下载时
if (!acceptRanges)
if(getDebug())
System.out.println("该地址不支持多线程下载");

//定义普通下载
DownloadThread thread = new DownloadThread(url, 0, contentLength, file, false);
thread.addDownloadListener(listener);
thread.start();
threads.add(thread);
return;


//每个请求的大小
long perThreadLength = contentLength / threadCount + 1;
long startPosition = 0;
long endPosition = perThreadLength;
//循环创建多个下载线程
do
if(endPosition >= contentLength)
endPosition = contentLength - 1;

DownloadThread thread = new DownloadThread(url, startPosition, endPosition, file);
thread.addDownloadListener(listener);
thread.start();
threads.add(thread);

startPosition = endPosition + 1;//此处加 1,从结束位置的下一个地方开始请求
endPosition += perThreadLength;
while (startPosition < contentLength);
1. /**
2. * 开始下载
3. * @throws Exception
4. */
5. public void startDown() throws
6. new
7. try
8. //获取下载文件信息
9. getDownloadFileInfo(httpClient);
10. //启动多个下载线程
11. startDownloadThread();
12. //开始监视下载数据
13. monitor();
14. catch
15. throw
16. finally
17. httpClient.getConnectionManager().shutdown();
18.
19.
20.
21. /**
22. * 获取下载文件信息
23. */
24. private void getDownloadFileInfo(HttpClient httpClient) throws
25. ClientProtocolException, Exception
26. new
27. HttpResponse response = httpClient.execute(httpHead);
28. //获取HTTP状态码
29. int
30.
31. if(statusCode != 200) throw new Exception("资源不存在!");
32. if(getDebug())
33. for(Header header : response.getAllHeaders())
34. ":"+header.getValue());
35.
36.
37.
38. //Content-Length
39. "Content-Length");
40. if(headers.length > 0)
41. 0].getValue());
42.
43. httpHead.abort();
44.
45. new
46. "Range", "bytes=0-"+(contentLength-1));
47. response = httpClient.execute(httpHead);
48. if(response.getStatusLine().getStatusCode() == 206)
49. true;
50.
51. httpHead.abort();
52.
53.
54. /**
55. * 启动多个下载线程
56. * @throws IOException
57. * @throws FileNotFoundException
58. */
59. private void startDownloadThread() throws
60. FileNotFoundException
61. //创建下载文件
62. new
63. file.createNewFile();
64. new RandomAccessFile(file, "rw");
65. raf.setLength(contentLength);
66. raf.close();
67.
68. //定义下载线程事件实现类
69. new
70. public void
71. //下载完一个片段后追加已下载字节数
72. synchronized
73. this.receivedCount += event.getCount();
74.
75.
76.
77. public void
78. //下载线程执行完毕后从主任务中移除
79. threads.remove(event.getTarget());
80. if(getDebug())
81. "剩余线程数:"+threads.size());
82.
83.
84. ;
85.
86. //不支持多线程下载时
87. if
88. if(getDebug())
89. "该地址不支持多线程下载");
90.
91. //定义普通下载
92. new DownloadThread(url, 0, contentLength, file, false);
93. thread.addDownloadListener(listener);
94. thread.start();
95. threads.add(thread);
96. return;
97.
98.
99. //每个请求的大小
100. long perThreadLength = contentLength / threadCount + 1;
101. long startPosition = 0;
102. long
103. //循环创建多个下载线程
104. do
105. if(endPosition >= contentLength)
106. 1;
107.
108. new
109. thread.addDownloadListener(listener);
110. thread.start();
111. threads.add(thread);
112.
113. 1;//此处加 1,从结束位置的下一个地方开始请求
114. endPosition += perThreadLength;
115. while
116.

 分段下载线程代码片段:

Java代码 ​​​​ 

1. 
2. /**
3. * 现在过程代码
4. */
5. public void
6. if(DownloadTask.getDebug())
7. "Start:" + startPosition + "-"
8.
9. new
10. try
11. new
12. if(isRange)//多线程下载
13. "Range", "bytes="+startPosition+"-"+endPosition);
14.
15. HttpResponse response = httpClient.execute(httpGet);
16. int
17. if(DownloadTask.getDebug())
18. for(Header header : response.getAllHeaders())
19. ":"+header.getValue());
20.
21. "statusCode:"
22.
23. if(statusCode == 206 || (statusCode == 200
24. InputStream inputStream = response.getEntity().getContent();
25. //创建随机读写类
26. new RandomAccessFile(file, "rw");
27. //跳到指定位置
28. outputStream.seek(startPosition);
29. int count = 0;byte[] buffer=new byte[1024];
30. while((count = inputStream.read(buffer, 0, buffer.length))>0)
31. 0, count);
32. //触发下载事件
33. new DownloadThreadEvent(this,count));
34.
35. outputStream.close();
36.
37. httpGet.abort();
38. catch
39. e.printStackTrace();
40. finally
41. //触发下载完成事件
42. new DownloadThreadEvent(this, endPosition));
43. if(DownloadTask.getDebug())
44. "End:" + startPosition + "-"
45.
46. httpClient.getConnectionManager().shutdown();
47.
48.
/**
* 现在过程代码
*/
public void run()
if(DownloadTask.getDebug())
System.out.println("Start:" + startPosition + "-" +endPosition);

HttpClient httpClient = new DefaultHttpClient();
try
HttpGet httpGet = new HttpGet(url);
if(isRange)//多线程下载
httpGet.addHeader("Range", "bytes="+startPosition+"-"+endPosition);

HttpResponse response = httpClient.execute(httpGet);
int statusCode = response.getStatusLine().getStatusCode();
if(DownloadTask.getDebug())
for(Header header : response.getAllHeaders())
System.out.println(header.getName()+":"+header.getValue());

System.out.println("statusCode:" + statusCode);

if(statusCode == 206 || (statusCode == 200 && !isRange))
InputStream inputStream = response.getEntity().getContent();
//创建随机读写类
RandomAccessFile outputStream = new RandomAccessFile(file, "rw");
//跳到指定位置
outputStream.seek(startPosition);
int count = 0;byte[] buffer=new byte[1024];
while((count = inputStream.read(buffer, 0, buffer.length))>0)
outputStream.write(buffer, 0, count);
//触发下载事件
fireAfterPerDown(new DownloadThreadEvent(this,count));

outputStream.close();

httpGet.abort();
catch (Exception e)
e.printStackTrace();
finally
//触发下载完成事件
fireDownCompleted(new DownloadThreadEvent(this, endPosition));
if(DownloadTask.getDebug())
System.out.println("End:" + startPosition + "-" +endPosition);

httpClient.getConnectionManager().shutdown();

/**
* 现在过程代码
*/
public void run()
if(DownloadTask.getDebug())
System.out.println("Start:" + startPosition + "-" +endPosition);

HttpClient httpClient = new DefaultHttpClient();
try
HttpGet httpGet = new HttpGet(url);
if(isRange)//多线程下载
httpGet.addHeader("Range", "bytes="+startPosition+"-"+endPosition);

HttpResponse response = httpClient.execute(httpGet);
int statusCode = response.getStatusLine().getStatusCode();
if(DownloadTask.getDebug())
for(Header header : response.getAllHeaders())
System.out.println(header.getName()+":"+header.getValue());

System.out.println("statusCode:" + statusCode);

if(statusCode == 206 || (statusCode == 200 && !isRange))
InputStream inputStream = response.getEntity().getContent();
//创建随机读写类
RandomAccessFile outputStream = new RandomAccessFile(file, "rw");
//跳到指定位置
outputStream.seek(startPosition);
int count = 0;byte[] buffer=new byte[1024];
while((count = inputStream.read(buffer, 0, buffer.length))>0)
outputStream.write(buffer, 0, count);
//触发下载事件
fireAfterPerDown(new DownloadThreadEvent(this,count));

outputStream.close();

httpGet.abort();
catch (Exception e)
e.printStackTrace();
finally
//触发下载完成事件
fireDownCompleted(new DownloadThreadEvent(this, endPosition));
if(DownloadTask.getDebug())
System.out.println("End:" + startPosition + "-" +endPosition);

httpClient.getConnectionManager().shutdown();

 附件说明:

1、Download.jar为编译好的可运行程序

2、Download.zip为Eclipse项目文件

3、运行截图

使用Apache


 

 

 

以上是关于使用Apache HttpClient实现多线程下载的小例子的主要内容,如果未能解决你的问题,请参考以下文章

HttpClient在多线程环境下踩坑总结

Apache HttpClient 制作多部分表单帖子

ThreadSafeClientConnManager用来支持多线程的使用http client

如何使用 HttpClient 进行多线程操作?

我的多线程 HttpClient 有啥问题吗?

基于apache —HttpClient的小爬虫获取网页内容