前言
因爲大作業要做瀏覽器,瀏覽器要帶下載器,所以現在先實現一個帶界面的多線程下載器類。
多線程下載器原理
之前寫過一個的【Java URLConnection類 實現多線程下載文件】,只是那個demo比較簡陋。。。
今天我們來加上圖形界面,其實下載部分的代碼都是複用過來的。。。。
要用到的swing組件
部分swing組件的介紹和【Java swing簡易瀏覽器(其一)頁面顯示,超鏈接跳轉與手動輸入URL跳轉】中的說明相同,這裏簡單說明一下
JFrame
主窗體,windows窗口,可縮放等等
JTextField
文字框輸入,我們需要用戶輸入或者改變
- 下載的url
- 保存地址
- 文件名
可以通過.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();
}
}