上篇日志中中写了多线程下载的基本技术路线和主要难点,这里贴出代码。
package Thread;
import java.net.URL;
import java.net.HttpURLConnection;
import java.io.*;
public class DownloadManager {
static final long unitSize=100*1024;//分配给每个下载线程的字节数
/**
* @param args
*2012-10-8
*void
* @throws IOException
*/
public static void main(String[] args) throws IOException {
if (args.length!=2) {
System.out.println("Usage:java DownloadManager URL localFileName");
return;
}
DownloadManager downloadManager=new DownloadManager();
downloadManager.doDownload(args[0], args[1]);
}
public void doDownload(String remoteFileUrl,String localFileName)throws IOException {
long fileSize=this.getRemoteFileSize(remoteFileUrl);
this.createFile(localFileName, 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,localFileName,offset,fileSize);
downloadThread.start();
}
else {//如果远程文件尺寸大于unitsize
for (int i = 1; i <=threadCount; i++) {
DownloadThread downloadThread=new DownloadThread(remoteFileUrl, localFileName, offset, unitSize);
downloadThread.start();
offset=offset+unitSize;
}
if(fileSize%unitSize!=0)//如果不能整除,则需要再创建一个线程下载剩余字节
{
DownloadThread downloadThread=new DownloadThread(remoteFileUrl, localFileName, offset, fileSize-unitSize*threadCount);
downloadThread.start();
}
}
}
//获取远程文件的大小
private long getRemoteFileSize(String remoteFileUrl)throws IOException
{
long result=0;
HttpURLConnection httpURLConnection=(HttpURLConnection)new URL(remoteFileUrl).openConnection();
httpURLConnection.setRequestMethod("HEAd");
for (int i = 0; i <10; i++) {
if (httpURLConnection.getHeaderField(i).equalsIgnoreCase("Content-Length")){
result=Long.parseLong(httpURLConnection.getHeaderField(i));
break;
}
}
return result;
}
//创建指定大小的文件
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 Thread;
import java.net.*;
import java.io.*;
public class DownloadThread extends Thread{
private String url=null;//待下载的文件
private String file=null;//本地存储路径
private long offset=0;//偏移量
private long length=0;//分配给本线程的下载字节数
public DownloadThread(String url,String file,long offset,long length) {
this.url=url;
this.file=file;
this.offset=offset;
this.length=length;
}
public void run() {
try {
HttpURLConnection httpURLConnection=(HttpURLConnection)new URL(this.url).openConnection();
httpURLConnection.setRequestMethod("GET");
httpURLConnection.setRequestProperty("RANGE", "bytes="+this.offset+"-"+(this.offset+this.length-1));
BufferedInputStream bis=new BufferedInputStream(httpURLConnection.getInputStream());
byte [] buff=new byte[1024];
int bytesRead;
while (-1!=(bytesRead=bis.read(buff, 0, buff.length))) {
this.writeFile(file, offset, buff, bytesRead);
this.offset=this.offset+bytesRead;
}
} catch (IOException e) {
e.printStackTrace();
}
}
//将字节数组以随机存取方式写入文件
//fileName是被写入的文件
//offset代表写入文件的位置偏移量
//bytes是待写入的字节数组
//realLength是实际需要写入的字节数(realLength<=bytes.length)
private void writeFile(String fileName,long offset,byte[] bytes,int realLength)throws IOException
{
File newFile =new File(fileName);
RandomAccessFile raf=new RandomAccessFile(newFile, "rw");
raf.seek(offset);
raf.write(bytes, 0, realLength);
raf.close();
}
}