Java swing 带界面和进度条的多线程下载器实现

前言

因为大作业要做浏览器,浏览器要带下载器,所以现在先实现一个带界面的多线程下载器类。

多线程下载器原理

之前写过一个的【Java URLConnection类 实现多线程下载文件】,只是那个demo比较简陋。。。

今天我们来加上图形界面,其实下载部分的代码都是复用过来的。。。。

要用到的swing组件

部分swing组件的介绍和【Java swing简易浏览器(其一)页面显示,超链接跳转与手动输入URL跳转】中的说明相同,这里简单说明一下

JFrame

主窗体,windows窗口,可缩放等等

JTextField

文字框输入,我们需要用户输入或者改变

  1. 下载的url
  2. 保存地址
  3. 文件名

可以通过.set / get Text方法设置或者取得里面的文字

JButton

按钮组件,我们点击后,开始创建下载线程并且开始下载。

可以简单的为一个JButton对象绑定被点击时候的事件

// 开始下载按钮 注册事件 点击的时候创建新下载线程
JButton startBtn = new JButton("开始下载");
startBtn.addActionListener(new ActionListener() {
	public void actionPerformed(ActionEvent e) {
		// 按钮按下的时候执行此处代码
	}
});

JLabel

一个文字显示的组件,可以支持显示html格式,我们用于显示一些提示信息

JProgressBar

这次的重点,进度条组件,使用方便,在构造函数的时候,指定最小值,最大值

JProgressBar bar = new JProgressBar(0, 100);

然后可以通过.setValue来改变当前进度条的值

布局

在这里插入图片描述
JFrame使用【网格布局】,通过以下代码就可以实现一个9行1列的网格

// 主窗体
JFrame jf = new JFrame("下载器");
jf.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
jf.setSize(512, 321);
jf.setLayout(new GridLayout(9, 1));

实现

因为进度条需要调用组件,所以我们创建一个类叫MultiThreadDownloader,将所有组件都设置为成员变量

此外我们还要存一个下载进程,方便我们查看下载进度并且更新进度条,因为所有下载进程共享一个进度,我们保留其中一个下载进程,就可以轻松获取了。

public class MultiThreadDownloader {
	
	String url;				// 目标url
	String savePath;		// 本地保存路径
	String fileName;		// 文件名
	int tnum = 5;			// 默认线程数目
	long totalLength;		// 总大小
	DownloadThread dth;		// 一个下载线程,用于查看下载进度
	JLabel  urlInputText;	// 文字:目标对象url
	JTextField urlInput;	// url输入框
	JLabel  pathInputText;	// 文字:本地保存路径
	JTextField pathInput;	// 保存路径输入框
	JLabel  nameInputText;	// 文字:文件名
	JTextField nameInput;	// 文件名输入框
	JLabel  barTex;			// 文字:进度条
	JProgressBar bar;		// 进度条
	JButton startBtn;		// 开始下载按钮
	ExecutorService pool = Executors.newCachedThreadPool();	// 线程池
	
	MultiThreadDownloader() {
		
	}
	
	MultiThreadDownloader(String url, String savePath, String fileName, int tnum) {
		this.url = url;
		this.tnum = tnum;
		this.savePath = savePath;
		this.fileName = fileName;
	}
...
下面是类的其他方法实现
}

我们编写一个download方法,该方法初始化我们的界面并且显示,除此之外,还为按钮注册按下事件, 事件内容里面执行的代码就是创建下载线程并且开始下载。

public void download() throws Exception {
		
	// 初始化组件,利用默认内容
	// url输入
	urlInputText = new JLabel ("目标对象URL");
	urlInput = new JTextField(url);
	// 路径输入
	pathInputText = new JLabel ("本地保存路径");
	pathInput = new JTextField(savePath);
	// 文件名输入
	nameInputText = new JLabel ("文件名");
	nameInput = new JTextField(fileName);
	// 进度条
	JLabel  barText = new JLabel ("进度条");
	bar = new JProgressBar(0, 100);
	
	// 开始下载按钮 注册事件 点击的时候创建新下载线程
	startBtn = new JButton("开始下载");
	startBtn.addActionListener(new ActionListener() {
		public void actionPerformed(ActionEvent e) {
			startBtn.setEnabled(false);	// 按钮禁用
			// 获得用户实际输入的内容
			savePath = pathInput.getText();
			fileName = nameInput.getText();
			url = urlInput.getText();
			try {
				totalLength = new URL(url).openConnection().getContentLength();
				bar.setMaximum((int)totalLength);
				long eachLength = totalLength / tnum;
				// 拓展名
				String ext = url.substring(url.lastIndexOf("."));
				// 创建下载线程
				for(int i=0; i<tnum; i++) {		
					long st = eachLength * i;
					long ed = eachLength * (i+1);
					if(i == tnum-1) {
						ed=Math.max(ed, totalLength);
					}
					// 建立URL连接
					URLConnection con = new URL(url).openConnection();
					con.setRequestProperty("Range", "bytes="+String.valueOf(st)+"-"+String.valueOf(ed));
					con.connect();
					// 打开文件流
					RandomAccessFile rf = new RandomAccessFile(savePath+fileName+ext, "rw");
					rf.seek(st);	// 文件流跳转到对应位置
					// 创建下载线程
					DownloadThread d =new DownloadThread(con, rf);
					pool.submit(d);
	
					// 保存最后一个线程 方便查看进度
					if(i==tnum-1) {
						dth = d;
					}
				}
				// 创建一个每500ms更新进度条的线程并且跑起来
				pool.submit(new Thread() {
					public void run() {
						while(true) {
							bar.setValue((int)dth.allcur);
							if(dth.allcur >= totalLength) {
								startBtn.setText("下载完成");
								break;
							}
							try {
								this.sleep(500);
							} catch (InterruptedException e) {
								e.printStackTrace();
							}
						}
					}
				});
				// 关闭线程池
				pool.shutdown();
			} catch(Exception e1) {
				e1.printStackTrace();
			}
		}
	});
	// 开始下载按钮 注册事件 -- 结束
	
	// 主窗体
	JFrame jf = new JFrame("下载器");
	jf.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
	jf.setSize(512, 321);
	jf.setLayout(new GridLayout(9, 1));
	jf.add(urlInputText);
	jf.add(urlInput);
	jf.add(pathInputText);
	jf.add(pathInput);
	jf.add(nameInputText);
	jf.add(nameInput);
	jf.add(barText);
	jf.add(bar);
	jf.add(startBtn);
	jf.show();
}

main函数只需创建相应的下载器,调用download方法即可。

这里我在我的个人网址上提供了两个链接用于测试下载,分别是稍大的pdf文件和小型txt文件。

http://www.szulrl.cn/browserTest/PDFFILE.zip
http://www.szulrl.cn/browserTest/TXTFILE.zip

public static void main(String[] args) throws Exception {
	MultiThreadDownloader dl = new MultiThreadDownloader(
			"http://www.szulrl.cn/browserTest/PDFFILE.zip", 
			"E:/MyEclipse/WorkSpace/Hello/src/homework/", 
			"下载",
			3);
	dl.download();
}

完整代码

/*
 * @Author : @李若龙 2018171028 @SZU @CSSE
 */
import java.net.*;
import java.util.*;
import java.util.concurrent.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.html.*;

class DownloadThread extends Thread {
	
	URLConnection con;
	RandomAccessFile rf;
	
	public static volatile long allcur = 0;
	public static boolean isReady = false;
	
	public DownloadThread() {
		
	}
	
	public DownloadThread(URLConnection con, RandomAccessFile rf) {
		this.con = con;
		this.rf = rf;
	}
	
	public void run() {
		try {
			// 获取输入输出流
			InputStream is = con.getInputStream();
			//System.out.printf("线程 获取文件成功\n");
			
			// 输入流内容写入输出流
			byte[] buf = new byte[1024];
			int len = -1;
			
			while((len=is.read(buf)) != -1) {
				rf.write(buf, 0, len);
				synchronized(new Object()) {
					allcur += (long)len;
				}
				// System.out.printf("%d / %d \n", allcur, 29210163);
			}
			rf.close();
			
		} catch(Exception e) {
			e.printStackTrace();
		}
	}
}

public class MultiThreadDownloader {
	
	String url;				// 目标url
	String savePath;		// 本地保存路径
	String fileName;		// 文件名
	int tnum = 5;			// 默认线程数目
	long totalLength;		// 总大小
	DownloadThread dth;		// 一个下载线程,用于查看下载进度
	JLabel  urlInputText;	// 文字:目标对象url
	JTextField urlInput;	// url输入框
	JLabel  pathInputText;	// 文字:本地保存路径
	JTextField pathInput;	// 保存路径输入框
	JLabel  nameInputText;	// 文字:文件名
	JTextField nameInput;	// 文件名输入框
	JLabel  barTex;			// 文字:进度条
	JProgressBar bar;		// 进度条
	JButton startBtn;		// 开始下载按钮
	ExecutorService pool = Executors.newCachedThreadPool();	// 线程池
	
	MultiThreadDownloader() {
		
	}
	
	MultiThreadDownloader(String url, String savePath, String fileName, int tnum) {
		this.url = url;
		this.tnum = tnum;
		this.savePath = savePath;
		this.fileName = fileName;
	}
	
	public void download() throws Exception {
		
		// 初始化组件,利用默认内容
		// url输入
		urlInputText = new JLabel ("目标对象URL");
		urlInput = new JTextField(url);
		// 路径输入
		pathInputText = new JLabel ("本地保存路径");
		pathInput = new JTextField(savePath);
		// 文件名输入
		nameInputText = new JLabel ("文件名");
		nameInput = new JTextField(fileName);
		// 进度条
		JLabel  barText = new JLabel ("进度条");
		bar = new JProgressBar(0, 100);
		
		// 开始下载按钮 注册事件 点击的时候创建新下载线程
		startBtn = new JButton("开始下载");
		startBtn.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				startBtn.setEnabled(false);	// 按钮禁用
				// 获得用户实际输入的内容
				savePath = pathInput.getText();
				fileName = nameInput.getText();
				url = urlInput.getText();
				try {
					totalLength = new URL(url).openConnection().getContentLength();
					bar.setMaximum((int)totalLength);
					long eachLength = totalLength / tnum;
					// 拓展名
					String ext = url.substring(url.lastIndexOf("."));
					// 创建下载线程
					for(int i=0; i<tnum; i++) {		
						long st = eachLength * i;
						long ed = eachLength * (i+1);
						if(i == tnum-1) {
							ed=Math.max(ed, totalLength);
						}
						// 建立URL连接
						URLConnection con = new URL(url).openConnection();
						con.setRequestProperty("Range", "bytes="+String.valueOf(st)+"-"+String.valueOf(ed));
						con.connect();
						// 打开文件流
						RandomAccessFile rf = new RandomAccessFile(savePath+fileName+ext, "rw");
						rf.seek(st);	// 文件流跳转到对应位置
						// 创建下载线程
						DownloadThread d =new DownloadThread(con, rf);
						pool.submit(d);
						if(i==tnum-1) {
							dth = d;
						}
					}
					// 创建一个每500ms更新进度条的线程
					pool.submit(new Thread() {
						public void run() {
							while(true) {
								//System.out.printf("%d / %d \n", dth.allcur, totalLength);
								bar.setValue((int)dth.allcur);
								if(dth.allcur >= totalLength) {
									startBtn.setText("下载完成");
									break;
								}
								try {
									this.sleep(500);
								} catch (InterruptedException e) {
									e.printStackTrace();
								}
							}
						}
					});
					// 关闭线程池
					pool.shutdown();
				} catch(Exception e1) {
					e1.printStackTrace();
				}
			}
		});
		// 开始下载按钮 注册事件 点击的时候创建新下载线程 -- 结束
		
		// 主窗体
		JFrame jf = new JFrame("下载器");
		jf.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
		jf.setSize(512, 321);
		jf.setLayout(new GridLayout(9, 1));
		jf.add(urlInputText);
		jf.add(urlInput);
		jf.add(pathInputText);
		jf.add(pathInput);
		jf.add(nameInputText);
		jf.add(nameInput);
		jf.add(barText);
		jf.add(bar);
		jf.add(startBtn);
		jf.show();
		
	}
	
	public static void main(String[] args) throws Exception {
		MultiThreadDownloader dl = new MultiThreadDownloader(
				"http://www.szulrl.cn/browserTest/PDFFILE.zip", 
				"E:/MyEclipse/WorkSpace/Hello/src/homework/", 
				"下载",
				3);
		dl.download();
	}
}

演示

在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章