由于Linux操作系统的兴起和Java语言的日渐成熟使用Java语言实现一个跨平台的外观一致的下载工具软件已成为可能网络蚂蚁是大家非常熟知的下载工具软件我采用Java语言实现了类似网络蚂蚁的基本功能的软件Jants本文介绍了一些技术实现要点
单线程直接获取网络文件
单线程直接获取网络文件的关键点是获取网络文件以确定基本方法的正确性它的初始代码的内容比较简单可以利用HTTP的基本知识进行设计它的基本原理是连接网络地址打开连接并获取输入流从输入流中读取数据实现代码(测试过程中使用的)如下
int data;//从输入流中获取数据
URL url=new URL();
//创建连接的地址
HttpURLConnection connection=urlopenConnection();
//打开连接
int responsCode=connection getResponseCode();
//返回Http的响应状态码
InputStream input=connectiongetInputStream();
//获取输入流
Systemoutprintln(responseCode:+responseCode);
While((data=inputread())!=)
Systemoutprintln(data);
//将获取的数据打到屏幕显示出来
单线程断点续传
大家常常有这样的体会:下载一个几十兆的文件时突然断线结果前功尽弃可以使用断点续传解决这个问题
基本原理
这里主要介绍一下断点续传的原理断点续传的原理很简单只是在HTTP请求上和一般的下载有所不同
所谓断点续传也就是要从文件已经下载的地方开始继续下载所以在客户端传给Web服务器的时候要多加一条信息——下载的起始位置且服务器返回的HTTP状态代码也从转变为
上述要点可以使用Java语言中的HttpURLConnection类中的setRequestProperty()方法来设置
断点续传的关键步骤
实现提交断点续传下载的起始位置
Java的Net包中提供了这种功能代码如下
URL url = new URL(;);
HttpURLConnection httpConnection=(HttpURLConnection) urlopenConnection();
//设置断点续传的开始位置
(RANGEbytes=);
//设置请求信息
(GET/downzip HTTP/);
//设置接受信息
(Acceptimage/gifimage/xxbitmapapplication/
msword*/*);
//设置连接信息
(ConnectionKeepAlive);
//获得输入流
InputStream input = ();
从输入流中取出的字节流就是downzip文件从字节开始的字节流
保存获得的字节流到文件中
由于文件的下载涉及到断点续传因此在保存文件的时候需要对文件进行随机读写特别是在多线程下载的过程中需要在写文件之前在文件中定位
在Java的IO包中的RandomAccessFile类可以满足这种设计需求该类在文件中定位指针时用到的方法是seek(Long)
操作相当简单假设从字节处开始保存文件代码如下
RandomAccess oSavedFile = newRandomAccessFile(downziprw);
//创建随机文件
long nPos = ;
//定位文件指针到nPos位置
oSavedFileseek(nPos);
byte[] b = new byte[];
int nRead;
//从输入流中读入字节流然后写到文件中
while((nRead=inputread(b)) > )
{ //input为网络输入流
oSavedFilewrite(bnRead);
}
保存已经下载的文件的长度值
由于在每次断开连接时都要保存已下载文件的长度且应进行永久保存因此将它保存到文件介质中这里采用的是对象序列化的方法——将要保存的内容序列化到一个临时文件中代码如下
long nStart;
//记录已经下载的字节数
File tempFile=new File(donwziptmp);
//创建临时文件
FileOutputStream file=new FileOutputStream(tempFile);
//创建文件输出流
ObjectOutputStream serialize=new ObjectOutputStream(file)
//创建文件序列化流
serializewriteObject(nStart);//序列化
serializeflush();
//刷新序列化流
fileclose();
//关闭文件输出流
serializeclose();
//关闭序列化流
多线程的断点续传
加入断点续传以后下载速度还没能得到提高为防止已下载文件数据的丢失也为提高网络文件的下载速度可在其中加入多线程虽然前两步已经基本实现似乎再加入多线程时应该比较容易但是并非如此在多线程的编程过程中需要考虑到线程的同步与互斥由于是多线程进行断点续传还要考虑记录多个断点位置且记录断点位置时也要考虑同步互斥等问题所有这些都使得这一步比较复杂
同步的考虑
同步的基本思想是避免多个线程访问同一个资源时出现问题由于多线程对同一个文件资源进行读写因此为了避免出现错误要进行读写控制——即同步Java中使用synchronized实现线程之间的同步Java是面向对象的语言它的资源是以对象的形式表现的因此Java同步机制的作用就是力图避免对对象的访问沖突
对需要同步的方法或代码段进行标记以实现同步需要用到关键字synchronized系统使用synchronized关键字声明的方法就是为其设置特殊的标记这个标记起着信号量的作用每当调用该方法时Java的运行系统都将进行检查以确认此标记的状态看相应的代码是否已经被调用执行如没有执行系统将把这个内部标记授予调用代码的线程方法运行结束后标记被释放在标记被释放之前任何其它的对象不得调用此方法
主要的同步代码如下(在下载数据保存入文件中时使用)
public synchronized int write(byte[] bint nStartint len){
int n=;
try{
rfwrite(bnStartlen);
//调用另一个类的方法向文件中写入数据
n=len;
}catch(IOException ioe){
ioeprintStackTrace();
}
return n;
}
在保存已下载的字节数时由于多个断点位置在不同的线程中记录所以必须在所有线程都结束时才能保存为此解决的办法是再开出一个线程用以持续监测是否所有的线程都已经结束若结束保存所有的断点位置否则继续监测同样在文件下载的线程中需要设置标志位以记录线程是否结束基本代码如下(在监测线程中使用)
stop=false;
while(!stop){
if(UtilitybStop[] && UtilitybStop[] && UtilitybStop[] && UtilitybStop[] && UtilitybStop[]){
Systemoutprintln(Serialize);
UtilityserializeOut();
//调用序列化函数以保存断点位置于文件中
javaantssetStopFalse();
javaantssetStartTrue();
stop=true;
}
断点数据的记录
笔者使用的是一个静态数组以记录断点位置由于有多个断点位置可采用一个函数进行统一保存
public static void serializeOut(){
try{
File tempFile=new File(getTempFileName()++tmp);
FileOutputStream file=new FileOutputStream(tempFile);
ObjectOutputStream serialize=new ObjectOutputStream(file);
for(int i=;i<;i++){
serializewriteObject(StringvalueOf(nStart[i]));
serializewriteObject(StringvalueOf(nStop[i]));
}
serializeflush();
fileclose();
serializeclose();
}catch(Exception e){
Systemoutprintln(etoString());
}
}
图形界面
该文件下载系统的图形界面与流行下载软件——网络蚂蚁很相似图形界面的实现使用了Swing包限于篇幅这里不再赘述Jants的图形界面如图和图所示
图 Jants主界面
图 Jants下载过程中的界面
发布
使用jar命令将所有的文件下载系统的class文件打包为javaantsjar文件并在其中加入Menifestmf文件指定MainClass
打包代码 jar cfv ** javaantsjar
运行代码javaw classpath javaantsjar Main