📄 download.java
字号:
package cn.xl;
import java.io.DataInputStream;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.math.BigDecimal;
import java.net.SocketException;
import java.net.URL;
import java.net.URLConnection;
import java.util.HashMap;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
import javax.swing.JTable;
/**
* 线程类,下载实现.
*/
class DownLoad extends Thread
{
private long readTotal=0; //已读的字节数.
private long point=0;//断点.
private String fileName="";//文件名.
private long fileLength=0;//文件长度.
private String hostName="";//对方主机名.
private String fileType=""; //部分属性应该设置为 "",而非null,避免抛出异常.
private String link=""; //链接地址.
public int row=0; //行索引.
private String saveFileName="";//另存为文件名.
private String saveDirectory="";//保存路径.
private URL url=null;//链接类.
private URLConnection URLC=null;
//输入输出流.
private InputStream ins=null;
private DataInputStream datainput=null;
/**
* 下载线程控制.1为运行状态,0为等待,-1为中断(适用于中断的历史任务).-2为手动删除.-3为加载正常历史记录.
*/
private int downLoadSign=-1;
/**
* 已完成的任务数.只有saveTaskEnd()方法使用此静态变量.
*/
private static int TASK_COMPLETE=0;
/**
* 已用时间.由线程锁保存其值.
*/
public long second=0;
/**
* 默认构造方法.
*/
public DownLoad(){}
/**
* 构造方法,新建下载任务时调用,参数包括 1,行号索引.2,链接路径,3,文件保存路径,4,被保存的文件名. 5,断点.
* @param row
* @param link
* @param saveDirectory
* @param saveFileName
* @param point
*/
DownLoad(int row,String link,String saveDirectory,String saveFileName,long point)
{
this.row=row;
this.link=link;
this.saveFileName=saveFileName;
this.saveDirectory=saveDirectory;
this.point=point;
}
/**
*下载实现.
*/
public void run()
{
DownLoad downLoad=(DownLoad)SaveRunTime.getLIST().get(row);//下载线程锁.
downLoad.downLoadSign=1;//开关.线程处于可运行状态.
CounterThread counter=(CounterThread)SaveRunTime.getTIMELIST().get(row);//计时锁.
JTable DOWNTABLE=SaveRunTime.getDOWN_TABLE();//表对象.控制表格变化.
try
{
datainput=new DataInputStream(ins);//二进制输入流.
RandomAccessFile raf=new RandomAccessFile(saveDirectory+saveFileName,"rw");//输出流.
raf.seek(point);//指针移至断点处.
int by=0;
try
{
by=datainput.read();//读一个字节.
}
catch (SocketException e)//可能抛出异常,只能中断线程,
{
//读不到数据.通知线程需要被中断.
System.out.println("读不到数据.");
downLoad.downLoadSign=-1;//下载线程将被中断.
}
System.out.println("文件长度:"+fileLength);
System.out.println("启动一个计时线程...");
CounterThread count_Speed=new CounterThread();
new Thread(count_Speed).start();//读到一个字节后启动计时线程.
SaveRunTime.getSPEED_LIST().add(count_Speed);//保存正在运行中的计时线程对象.
while(by!=-1)
{
if(downLoad.downLoadSign!=1)
{
synchronized(downLoad)
{
if(downLoad.downLoadSign==0)//线程进入等待.
{
counter.timeSign=0; //通知计时线程进入等待.
count_Speed.timeSign=-1;//通知即时速度计算器,不计算此任务的速度.
downLoad.wait();//下载线程进入等待.
synchronized (counter)//醒来时唤醒计时线程.
{
counter.notify();
}
//设置下载图标.
DOWNTABLE.setValueAt(new XDownTable().getImageIcon("/image/ing.png"), row, 0);
downLoad.downLoadSign=1;//设置开关.
count_Speed.timeSign=1;
}
else if(downLoad.downLoadSign==-1)//线程需要被中断.下载异常中断时调用.
{
//连接中断图标.
DOWNTABLE.setValueAt(new XDownTable().getImageIcon("/image/die.png"), row, 0);
count_Speed.timeSign=-1;
counter.timeSign=-1;//通知计时线程中断.
Thread.currentThread().interrupt();//下载线程被中断.
System.out.println("得不到链接,下载线程中断."+Thread.currentThread().isInterrupted());
return;
}
else if(downLoad.downLoadSign==-2)//手动强制删除任务,只有当任务在运行时才被调用.
{
count_Speed.timeSign=-1;
counter.timeSign=-2; //通知计时线程需要中断.
Thread.currentThread().interrupt();//下载线程被中断.
return;
}
}
}
try
{
raf.write(by);//写一个字节.
by=datainput.read();//读一个字节,这里会抛出SocketExcetpion.
readTotal++;//累计读到的字节,用于计时线程计算进度.
}
catch (SocketException e)
{
//读不到数据.通知线程需要被中断.
System.out.println("读不到数据.");
downLoad.downLoadSign=-1;//下载线程将被中断.
}
}
//for循环完成.
datainput.close();
raf.close();//关闭流.
saveTaskEnd(downLoad);//保存完成的任务信息到 已下载表.
count_Speed.timeSign=-1;//通知即时速度计算器.
counter.timeSign=-3; //通知计时线程,下载完成.
Thread.currentThread().interrupt();//中断线程.
System.out.println("下载线程中断:"+Thread.currentThread().isInterrupted());
System.out.println("下载完成");
}
catch(Exception ex)
{
ex.printStackTrace();
}
}
/**
* 单个下载任务的基本信息.得到连接时返回true ,此方法会给类的字段赋值.
* 包括.link,url,URLC,ins,fileName,fileLength,fileType,hostName.
* 如果下载线程没有启动,则部分字段值为null,
* 方法由测试线程调用.
*/
public boolean fileInfo()
{
JTable DOWNINFO_TABLE=SaveRunTime.getDOWN_INFOTABLE();
DOWNINFO_TABLE.setValueAt("正在获取连接......", 0, 0);
DOWNINFO_TABLE.setValueAt("",1,0);
DOWNINFO_TABLE.setValueAt("",1,1);
DOWNINFO_TABLE.setValueAt("",2,0);
DOWNINFO_TABLE.setValueAt("",2,1);
DOWNINFO_TABLE.setValueAt("",3,0);
DOWNINFO_TABLE.setValueAt("",3,1);
DOWNINFO_TABLE.setValueAt("",4,0);
DOWNINFO_TABLE.setValueAt("",4,1);
DOWNINFO_TABLE.setValueAt("",5,0);
DOWNINFO_TABLE.setValueAt("",5,1);
DOWNINFO_TABLE.setValueAt("",6,0);
DOWNINFO_TABLE.setValueAt("",6,1);
try
{
url=new URL(link);
URLC=url.openConnection();//得到链接. //断点.
URLC.setRequestProperty("RANGE", "bytes="+point+"-");
ins=URLC.getInputStream();
//System.out.println(URLC.getHeaderFields());393696992
//文件名.直接取自链接地址.
fileName=interceptURL(url.getFile());
DOWNINFO_TABLE.setValueAt("文件名称", 1, 0);
DOWNINFO_TABLE.setValueAt(fileName, 1, 1);
//文件长度.
fileLength=URLC.getContentLength()+point;
float f=((float)fileLength)/((float)(1024*1024)+(point/(1024f*1024f)));
DOWNINFO_TABLE.setValueAt("文件长度", 2, 0);
DOWNINFO_TABLE.setValueAt(new CounterThread().interceptFolat(f)+"M", 2, 1);
//文件类型.
fileType=URLC.getContentType();
if(fileType==null||"".equals(fileType))
{
fileType="未知";
}
DOWNINFO_TABLE.setValueAt("文件类型", 3, 0);
DOWNINFO_TABLE.setValueAt(fileType, 3, 1);
//保存路径.
DOWNINFO_TABLE.setValueAt("保存路径", 4, 0);
DOWNINFO_TABLE.setValueAt(saveDirectory, 4, 1);
//链接地址.
DOWNINFO_TABLE.setValueAt("链接地址", 5, 0);
DOWNINFO_TABLE.setValueAt(link, 5, 1);
//对方主机名.
hostName=url.getHost();
DOWNINFO_TABLE.setValueAt("对方主机", 6, 0);
DOWNINFO_TABLE.setValueAt(hostName, 6, 1);
//没有异常情况时;添加键值对.
HashMap<String,String> hm=new HashMap<String,String>(7);
hm.put("正在获取链接...","");
hm.put("文件名称",saveFileName);
hm.put("文件长度",new CounterThread().interceptFolat(f)+"M");
hm.put("文件类型",fileType);
hm.put("保存路径",saveDirectory+saveFileName);
hm.put("链接地址",link);
hm.put("对方主机",hostName);
SaveRunTime.getSAVE_HASHMAP().add(hm);
return true;
}
catch (Exception e)
{
return false;
}
}
/**
* 线程类,计算下载器的即时速度.程序最早开始运行时启动此线程.一直运行至程序被关闭.
*/
class CounterSpeed implements Runnable
{
public void run()
{
/* List中保存的是计时线程对象.但并不用于控制计时.所以在程序运行时对timeSign的属性值的改变,仅仅是
* 通知即时速度计算器决定是否计算此线程的下载速度.
* */
long speed=0;
for(;;)
{
List<Object> counter=SaveRunTime.getSPEED_LIST();//对象在启动计时线程后被保存.
for(int i=0;i<counter.size();i++)
{
CounterThread count_Speed=(CounterThread)counter.get(i);
if(count_Speed.timeSign==1)
{
speed=speed+count_Speed.count;
}
}
//控制Jlabel的变化.
SaveRunTime.getSPEED_JLABEL().setText("\u901f\u5ea6:"+new CounterThread().interceptFolat(speed/(1024f))+"KB/S");
speed=0;//归零.
try
{
Thread.sleep(500);//睡眠半秒.
} catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
}
/**
* 线程类,由下载线程调用.用于计算单个任务的 进度,速度,剩余时间,已用时间.
*
*/
class CounterThread implements Runnable
{
/**
*线程控制开关.四个有效值:1.运行.0,等待.-1,中断.-2,手动删除.-3,任务完成.
*/
public int timeSign=1;
/**
* 每隔一秒,计算差值.得到下载速度.
*/
private long count=0;
/**
* 临时变量.保存一秒之前的读到字节数.
*/
private long temp=0;
/**
* 任务进度.
*/
private String downLoadSchedule=null;
/**
* 具体实现,下载线程读到第一个字节后启动.
*/
public void run()
{
CounterThread inner=(CounterThread)SaveRunTime.getTIMELIST().get(row);//锁.
final JTable DOWNTABLE=SaveRunTime.getDOWN_TABLE();//表格信息控制.
//不需要随时更新的列.
DOWNTABLE.setValueAt(saveFileName, row, 1);//文件名
//正在下载图标.可以被下载线程更改.
DOWNTABLE.setValueAt(new XDownTable().getImageIcon("/image/ing.png"), row, 0);
DOWNTABLE.setValueAt(fileType, row, 6);//文件类型.
/**
* 观察下载任务的进度,速度变化.
*/
class ChildObservable extends Observable
{
/**
* 观察每间隔一秒后读到字节的变化.
* @param count
*/
public void compute(long count)
{
super.setChanged();
super.notifyObservers(count);
}
}
class ImObserver implements Observer
{
/**
* 更新.
*/
public void update(Observable o, Object arg)
{
//下载进度.
CounterThread.this.getProgress();
DOWNTABLE.setValueAt(CounterThread.this.downLoadSchedule, row, 2);
//下载速度.
DOWNTABLE.setValueAt(CounterThread.this.interceptFolat(count/1024f)+"KB/s", row, 3);
//所需时间.(文件长度-已经下载)/每秒速度
DOWNTABLE.setValueAt(CounterThread.this.getTime((fileLength-readTotal)/((float)count)),row,4);
//已用时间
DOWNTABLE.setValueAt(CounterThread.this.getTime(((DownLoad)SaveRunTime.getLIST().get(row)).second++), row, 5);
}
}
ChildObservable child=new ChildObservable();
child.addObserver(new ImObserver());
for(;;)
{
if(inner.timeSign==1) //运行状态.
{
try
{
temp=readTotal;
//线程睡眠一秒.
Thread.sleep(1000);
//得到每秒的下载量.
count=readTotal-temp;
child.compute(count);//观察者.
} catch (Exception e)
{
e.printStackTrace();
}
}
else
{
if(inner.timeSign==0) //等待状态.由下载线程进入等待之前通知.
{
synchronized(inner)//暂停计时.
{
try
{ //表格信息.
DOWNTABLE.setValueAt("--", row, 3);
DOWNTABLE.setValueAt("--", row, 4);
System.out.println("暂停计时");
inner.wait(); //线程进入等待.
inner.timeSign=1;//醒来时设置开关.
} catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
else if(inner.timeSign==-1) //异常中止状态.(SocketException 中途读不到数据)
{
DOWNTABLE.setValueAt("--", row, 3); //表格信息.
DOWNTABLE.setValueAt("99:0:0", row, 4);
inner.timeSign=1;//停止之后,可能会再次被启动.
try
{
new XmlOperation().writerTaskInfo(); //写xml
} catch (Exception e)
{
e.printStackTrace();
}
Thread.currentThread().interrupt();//中断线程.
System.out.println("计时线程被中断..."+Thread.currentThread().isInterrupted());
return;
}
else if(inner.timeSign==-2) //手动删除任务.
{
DownLoad downLoad=(DownLoad)(SaveRunTime.getLIST().get(row));
downLoad.taskEnd(row);//删除.
try
{
new XmlOperation().writerTaskInfo(); //删除后写xml.
} catch (Exception e)
{
e.printStackTrace();
}
Thread.currentThread().interrupt();//中断线程.
return;
}
else if(inner.timeSign==-3) //有任务完成.由下载线程通知.
{
//下载完成,清空行内信息.
DownLoad.this.taskEnd(row);
try
{
new XmlOperation().writerTaskInfo();
} catch (Exception e)
{
e.printStackTrace();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -