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();
	}
}

演示

在這裏插入圖片描述

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