⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 download.java

📁 仿迅雷下载器,http下载,多线程.多任务
💻 JAVA
📖 第 1 页 / 共 2 页
字号:
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 + -