`

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

    博客分类:
  • Java
阅读更多

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

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

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

 

原理二:多线程下载原理

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

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

3、通过主任务创建多个分段下载线程,分段下载文件,然后用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);
	}

 分段下载线程代码片段:

	
	/**
	 * 现在过程代码
	 */
	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、运行截图

 

  • 大小: 36.3 KB
14
1
分享到:
评论
5 楼 u012935452 2016-04-04  
    SHOUJIAOLA
4 楼 guanggsanguo 2015-02-12  
学习!赞一个!
3 楼 zjc198805 2013-09-11  
当网速不佳的时候,总是会下载失败?特别是再wifi的情况下,我在android中使用的你的代码的!
2 楼 xiaofeiyang 2012-04-10  
学习了 楼主大牛。
1 楼 weishen 2012-01-06  
牛人,请受一拜

相关推荐

    org.apache.commons.httpclient 远程下载文件

    在实际应用中,你可能需要考虑更多的细节,如错误处理、重试机制、进度显示、多线程下载等。此外,Apache HttpClient库已经被弃用,现在推荐使用Java 7及更高版本内置的`java.net.HttpURLConnection`或更现代的库如...

    java实现HttpClient异步请求资源的方法

    本篇文章将详细介绍如何使用Java的HttpClient实现异步请求资源。 首先,让我们了解什么是异步请求。在同步请求中,调用一个API或发送一个HTTP请求后,程序会等待响应返回,然后继续执行后续代码。而异步请求则不同...

    HttpClient介绍和使用文档

    HttpClient还支持重试机制、连接池管理、多线程请求等高级特性,使得在处理大量HTTP请求时更加高效和稳定。此外,HttpClient可以与其他Apache Commons库,如HttpCore、HttpComponents等配合使用,实现更复杂的网络...

    HttpClient包及例子

    例如,它支持多线程、连接池管理、重试策略、身份验证和压缩等高级特性。 使用HttpClient,你可以轻松地模拟GET请求。GET请求通常用于从服务器获取资源,比如网页或API数据。以下是一个简单的示例: ```java ...

    httpClient组合包.zip

    此外,HttpClient还支持异步操作,可以在多线程环境中高效地处理并发请求。 2. **httpcore-4.4.12.jar**:这是HttpClient的核心库,包含了HTTP协议的基本组件,如连接管理、请求和响应模型、编码器和解码器等。...

    httpClient完整请求Demo

    这通常在程序开始时完成,因为HttpClient是线程安全的,可以重复使用。 ```java CloseableHttpClient httpClient = HttpClients.createDefault(); ``` 2. **创建HttpGet或HttpPost对象**: 根据你的需求,你可以...

    HTTP异步工具包(HttpClient实现)

    3. **多线程处理回调**:在处理回调时,可以考虑使用线程池来避免主线程被阻塞,提高并发处理能力。 4. **错误处理**:在`failed`方法中,应正确处理异常,如重试策略、错误日志记录等。 四、总结 HttpClient提供...

    老罗http章例子

    1. **连接管理**:HttpClient支持多线程环境下的连接管理,可以设置连接池大小,有效控制并发连接数,提高性能。 2. **重定向处理**:HttpClient自动处理服务器返回的3xx状态码,根据Location头进行重定向。 3. **...

    HTTPClient4的典型小例子

    **HTTPClient4简介** ...**连接管理和多线程** HTTPClient4允许我们自定义连接管理器,以处理并发请求和连接池。`PoolingHttpClientConnectionManager`是常用的连接管理器,它可以复用TCP连接,提高性能: ```java ...

    一个面向对象风格的JavaHTTPClient

    Apache HttpClient库提供了丰富的API来构建复杂的HTTP请求,支持多线程、连接池和更强大的重试策略。以下是一个使用Apache HttpClient的例子: ```java CloseableHttpClient httpClient = HttpClients.create...

    httpClient 调用远程接口 获取数据到本地文件夹

    HttpClient支持多线程请求,可以使用ExecutorService来管理线程池。同时,注意防止对服务器的过度请求,遵守其速率限制政策,避免被封IP。 7. **安全性与身份验证**: 访问某些远程接口可能需要身份验证。...

    httpClient调用远程接口获取数据到本地文件夹

    为了提高效率,可以使用多线程并发处理多个请求,或者使用连接池管理HttpClient的连接,减少建立新连接的时间。 10. **测试与运行**: 提供一个测试类,可以直接运行以验证功能。测试类应包含初始化HttpClient、...

    HttpClient使用示例教程

    7. **连接管理器**:用于多线程应用,可设置最大连接数和每个主机的最大连接数,以及自动关闭过期连接。 8. **Cookie处理**:自动处理Set-Cookie头,并支持自定义Cookie策略。 9. **流控制**:优化了请求输出流和...

    HttpClient

    - **多线程**:HttpClient支持多线程执行请求,适合大量并发请求的场景。 - **连接管理**:HttpClient的HttpConnectionManager用于管理连接池,优化性能和资源利用。 - **重试策略**:可以通过设置重试处理器...

    Java爬虫小例子,爬取小网站,突破防盗链下载图片

    - **线程与异步处理**:为提高爬取效率,通常会使用多线程或异步处理技术,如Java的ExecutorService。 2. **网页解析**: - **CSS选择器**:通过CSS选择器定位网页元素,例如,用`doc.select("img")`找到所有图片...

    HttpClient.rar

    Apache HttpClient是一个成熟的HTTP客户端API,包含了许多高级特性,如连接管理、多线程支持、自定义编码解码等。 Apache HttpClient的使用通常涉及创建`CloseableHttpClient`实例,配置请求对象`HttpGet`或`...

    HttpClient中的client和server相互调用的两个例子

    但需注意线程安全问题,避免多个线程共享同一个HttpClient实例。 - 错误处理:正确处理异常,如网络中断、超时、重定向等。 - 安全性:在处理敏感信息时,确保使用HTTPS协议,设置SSL/TLS上下文。 6. **最佳实践*...

    httpClient官方实例

    `MultiThreadedExample_java.htm`可能包含有关如何在多线程环境中使用HttpClient的例子。在并发场景下,HttpClient能确保线程安全并有效利用资源。 4. **Cookie管理**: `CookieDemoApp_java.htm`可能会展示如何...

Global site tag (gtag.js) - Google Analytics