网上类似的文章很多,参考了很多人的,大部分人都是用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
分享到:
相关推荐
在实际应用中,你可能需要考虑更多的细节,如错误处理、重试机制、进度显示、多线程下载等。此外,Apache HttpClient库已经被弃用,现在推荐使用Java 7及更高版本内置的`java.net.HttpURLConnection`或更现代的库如...
本篇文章将详细介绍如何使用Java的HttpClient实现异步请求资源。 首先,让我们了解什么是异步请求。在同步请求中,调用一个API或发送一个HTTP请求后,程序会等待响应返回,然后继续执行后续代码。而异步请求则不同...
HttpClient还支持重试机制、连接池管理、多线程请求等高级特性,使得在处理大量HTTP请求时更加高效和稳定。此外,HttpClient可以与其他Apache Commons库,如HttpCore、HttpComponents等配合使用,实现更复杂的网络...
例如,它支持多线程、连接池管理、重试策略、身份验证和压缩等高级特性。 使用HttpClient,你可以轻松地模拟GET请求。GET请求通常用于从服务器获取资源,比如网页或API数据。以下是一个简单的示例: ```java ...
此外,HttpClient还支持异步操作,可以在多线程环境中高效地处理并发请求。 2. **httpcore-4.4.12.jar**:这是HttpClient的核心库,包含了HTTP协议的基本组件,如连接管理、请求和响应模型、编码器和解码器等。...
这通常在程序开始时完成,因为HttpClient是线程安全的,可以重复使用。 ```java CloseableHttpClient httpClient = HttpClients.createDefault(); ``` 2. **创建HttpGet或HttpPost对象**: 根据你的需求,你可以...
3. **多线程处理回调**:在处理回调时,可以考虑使用线程池来避免主线程被阻塞,提高并发处理能力。 4. **错误处理**:在`failed`方法中,应正确处理异常,如重试策略、错误日志记录等。 四、总结 HttpClient提供...
1. **连接管理**:HttpClient支持多线程环境下的连接管理,可以设置连接池大小,有效控制并发连接数,提高性能。 2. **重定向处理**:HttpClient自动处理服务器返回的3xx状态码,根据Location头进行重定向。 3. **...
**HTTPClient4简介** ...**连接管理和多线程** HTTPClient4允许我们自定义连接管理器,以处理并发请求和连接池。`PoolingHttpClientConnectionManager`是常用的连接管理器,它可以复用TCP连接,提高性能: ```java ...
Apache HttpClient库提供了丰富的API来构建复杂的HTTP请求,支持多线程、连接池和更强大的重试策略。以下是一个使用Apache HttpClient的例子: ```java CloseableHttpClient httpClient = HttpClients.create...
HttpClient支持多线程请求,可以使用ExecutorService来管理线程池。同时,注意防止对服务器的过度请求,遵守其速率限制政策,避免被封IP。 7. **安全性与身份验证**: 访问某些远程接口可能需要身份验证。...
为了提高效率,可以使用多线程并发处理多个请求,或者使用连接池管理HttpClient的连接,减少建立新连接的时间。 10. **测试与运行**: 提供一个测试类,可以直接运行以验证功能。测试类应包含初始化HttpClient、...
7. **连接管理器**:用于多线程应用,可设置最大连接数和每个主机的最大连接数,以及自动关闭过期连接。 8. **Cookie处理**:自动处理Set-Cookie头,并支持自定义Cookie策略。 9. **流控制**:优化了请求输出流和...
- **多线程**:HttpClient支持多线程执行请求,适合大量并发请求的场景。 - **连接管理**:HttpClient的HttpConnectionManager用于管理连接池,优化性能和资源利用。 - **重试策略**:可以通过设置重试处理器...
- **线程与异步处理**:为提高爬取效率,通常会使用多线程或异步处理技术,如Java的ExecutorService。 2. **网页解析**: - **CSS选择器**:通过CSS选择器定位网页元素,例如,用`doc.select("img")`找到所有图片...
Apache HttpClient是一个成熟的HTTP客户端API,包含了许多高级特性,如连接管理、多线程支持、自定义编码解码等。 Apache HttpClient的使用通常涉及创建`CloseableHttpClient`实例,配置请求对象`HttpGet`或`...
但需注意线程安全问题,避免多个线程共享同一个HttpClient实例。 - 错误处理:正确处理异常,如网络中断、超时、重定向等。 - 安全性:在处理敏感信息时,确保使用HTTPS协议,设置SSL/TLS上下文。 6. **最佳实践*...
`MultiThreadedExample_java.htm`可能包含有关如何在多线程环境中使用HttpClient的例子。在并发场景下,HttpClient能确保线程安全并有效利用资源。 4. **Cookie管理**: `CookieDemoApp_java.htm`可能会展示如何...