实现原理:
用httpConnection.setRequestProperty("RANGE" , "bytes= xxx -xxx" );获取指定的数据块
用RandomAccessFile实现文件随机访问,写入指定数据块到文件.
关键事项:获取远程文件大小,根据文件大小确定下载线程个数(可固定线程数,也可固定每个线程下载数据块大小,用户自己决定).
package org.ifusing.down;
import java.io.*;
import java.net.*;
/**
* 文件下载管理类
*/
public class DownLoadManager {
/**
* 每个线程下载的字节数
*/
static final long unitSize = 100 * 1024;
/**
* 启动多个线程下载文件
*/
public void doDownload(String remoteFileUrl)
throws IOException {
String fileName = new URL(remoteFileUrl).getFile();
fileName = fileName.substring(fileName.lastIndexOf( "/" )+1,fileName.length()).replace( "%20" , " " );
long fileSize = this .getRemoteFileSize(remoteFileUrl);
if (fileSize == 0){
return ;
}
this .createFile(fileName, fileSize);
long threadCount = fileSize / unitSize ;
System. out .println( " 共启动 "
+ (fileSize % unitSize == 0 ? threadCount : threadCount + 1)
+ " 个线程" );
long offset = 0;
if (fileSize <= unitSize ) { // 如果远程文件尺寸小于等于unitSize
DownloadThread downloadThread = new DownloadThread(remoteFileUrl,
fileName, offset, fileSize);
downloadThread.start();
} else { // 如果远程文件尺寸大于unitSize
for ( int i = 1; i <= threadCount; i++) {
DownloadThread downloadThread = new DownloadThread(
remoteFileUrl, fileName, offset, unitSize );
downloadThread.start();
offset = offset + unitSize ;
}
if (fileSize % unitSize != 0) { // 如果不能整除,则需要再创建一个线程下载剩余字节
DownloadThread downloadThread = new DownloadThread(
remoteFileUrl, fileName, offset, fileSize
- unitSize * threadCount);
downloadThread.start();
}
}
}
/**
* 获取远程文件尺寸
*/
private long getRemoteFileSize(String remoteFileUrl) throws IOException {
long fileSize = 0;
HttpURLConnection httpConnection = (HttpURLConnection) new URL(
remoteFileUrl).openConnection();
httpConnection.setRequestMethod( "HEAD" );
int responseCode = httpConnection.getResponseCode();
if (responseCode >= 400){
System. out .println( "Web 服务器响应错误!" );
return 0;
}
String sHeader;
for ( int i=1;;i++){
sHeader = httpConnection.getHeaderFieldKey(i);
if (sHeader != null && sHeader.equals( "Content-Length" )){
System. out .println( " 文件大小ContentLength:" +httpConnection.getContentLength());
fileSize = Long.parseLong (httpConnection.getHeaderField(sHeader));
break ;
}
}
return fileSize;
}
/**
* 创建指定大小的文件
*/
private void createFile(String fileName, long fileSize) throws IOException {
File newFile = new File(fileName);
RandomAccessFile raf = new RandomAccessFile (newFile, "rw" );
raf.setLength(fileSize);
raf.close();
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
package org.ifusing.down ;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
/**
* 负责文件下载的类
*/
public class DownloadThread extends Thread {
/**
* 待下载的文件
*/
private String url = null ;
/**
* 本地文件名
*/
private String fileName = null ;
/**
* 偏移量
*/
private long offset = 0;
/**
* 分配给本线程的下载字节数
*/
private long length = 0;
/**
* @param url 下载文件地址
* @param fileName 另存文件名
* @param offset 本线程下载偏移量
* @param length 本线程下载长度
*
* @author Angus.wang
* */
public DownloadThread(String url, String file, long offset, long length) {
this . url = url;
this . fileName = file;
this . offset = offset;
this . length = length;
System. out .println( " 偏移量=" + offset + "; 字节数=" + length);
}
public void run() {
try {
HttpURLConnection httpConnection = (HttpURLConnection) new URL(
this . url ).openConnection();
httpConnection.setRequestMethod( "GET" );
httpConnection.setRequestProperty( "RANGE" , "bytes=" + this . offset
+ "-" + ( this . offset + this . length - 1));
System. out .println( "RANGE bytes=" + this . offset + "-" + ( this . offset + this . length - 1) http://www.kcvg.cn 原创);
BufferedInputStream bis = new BufferedInputStream(httpConnection
.getInputStream());
byte [] buff = new byte [1024];
int bytesRead;
File newFile = new File( fileName );
RandomAccessFile raf = new RandomAccessFile(newFile, "rw" );
while ((bytesRead = bis.read(buff, 0, buff. length )) != -1) {
raf.seek( this . offset );
raf.write(buff, 0, bytesRead);
this . offset = this . offset + bytesRead;
}
raf.close();
bis.close();
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
package org.ifusing.downtest ;
/**
* 多线程文件下载测试
* */
public class FileDownloadTest {
/**
* @param args
*/
public static void main(String[] args) {
try {
String remoteFileUrl = "http://dl_dir.qq.com/qqfile/qq/QQ2009/QQ2009SP5.exe" ;
DownLoadManager downLoadManager = new DownLoadManager();
downLoadManager.doDownload(remoteFileUrl);
} catch (Exception e){
e.printStackTrace();
}
}
}