目錄
前言
大作業繼續。
之前的內容
【Java swing簡易瀏覽器(其一)頁面顯示,超鏈接跳轉與手動輸入URL跳轉】
【Java swing簡易瀏覽器(其二)前進後退與收藏夾實現】
今天來實現最後幾個功能。
實現思路
這裏只介紹實現思路,代碼的改動在【代碼實現】部分
下載器
之前做了準備,我們使用之前寫好的代碼即可
我們在爲JEditorPane組件綁定超鏈接事件的時候,跳轉的時候判斷一下地址是否合法
- 如果符合規格,即以http包頭的content type是否是
text/html
,是則跳轉 - 如果不符合,我們利用之前寫好的下載器類,傳入鏈接地址url並且打開下載器
智能搜索欄
我們知道,如果在瀏覽器的地址欄輸入一些名詞,而不是地址,瀏覽器會自動幫我們跳轉到搜索頁面。
瞭解這一點之後,我們在頁面跳轉之前,判斷輸入框的url是否符合規範,即以http打頭,如果符合就跳轉,否則修改鏈接,跳轉到必應的搜索頁面(百度的話,訪問有bug)
關於必應搜索地址參數:
https://cn.bing.com/search?q=你要搜索的詞彙
郵件發送
之前也造過類似的輪子
【Java socket通過smtp協議 向郵箱服務器請求以實現發送郵件】
我們在其基礎上,加上圖形界面即可。 圖形界面的代碼後面會放出。
代碼實現
注意,以下代碼的修改,都是基於上兩次實驗的代碼來修改的,爲了篇幅,我這裏只放出修改的代碼
完整代碼在下面【完整代碼】部分會貼出來
下載器
因爲我們之前造過輪子了,所以使用以下語句就可以創建一個圖形界面下載器。
MultiThreadDownloader dl = new MultiThreadDownloader(
hyplink,
"E:/MyEclipse/WorkSpace/Hello/src/homework/",
"下載",
3);
dl.download();
我們修改JEditorPane綁定超鏈接的事件的代碼,修改爲以下的代碼:
// 添加超鏈接點擊事件回調函數 並將JEditorPane的頁面改爲超鏈接的頁面
jep.addHyperlinkListener(new HyperlinkListener() {
public void hyperlinkUpdate(HyperlinkEvent event) {
if(event.getEventType()==HyperlinkEvent.EventType.ACTIVATED) {
// 獲取超鏈接目的地址
String hyplink = event.getURL().toString();
try {
// 判斷content type 如果是text/html就訪問 否則下載
String ctype = new URL(hyplink).openConnection().getContentType().substring(0, 4);
if(ctype.equals("text")) {
newPage(hyplink);
} else {
MultiThreadDownloader dl = new MultiThreadDownloader(
hyplink,
"E:/MyEclipse/WorkSpace/Hello/src/homework/",
"下載",
3);
dl.download();
}
} catch (Exception e) {
e.printStackTrace();
newPage(hyplink);
}
}
}
});
智能搜索欄
同樣修改按下【點我訪問網頁】按鈕的按下事件中執行的內容,我們判斷輸入是否是一個http鏈接,是則跳轉,否則搜索
// 訪問按鈕 綁定訪問按鈕點擊事件 從JTextField輸入框獲取URL並且訪問
goBtn = new JButton("點我訪問網頁");
goBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// 獲取輸入的地址欄的地址
String input = jtf.getText();
if(input.equals(curPage)) {
refreshPage();
} else if(input.length()>4 && input.substring(0, 4).equals("http")){
newPage(jtf.getText());
} else {
// 跳轉到必應搜索
newPage("https://cn.bing.com/search?q=" + jtf.getText());
}
}
});
郵件發送
我們實現一個類專門來發郵件,叫做EmailSender,其中send方法是之前造輪子的時候就寫過了,故我們爲其封裝一個新方法sendInWindow(),即利用圖形界面發送郵件。
在sendInWindow方法裏面,會調用send方法來真正發送郵件。
void sendInWindow() {
try {
destInputText = new JLabel("目的郵箱地址");
destInput = new JTextField("填寫您的郵件目的地址");
textInputText = new JLabel("內容");
textInput = new JTextField("這是一封自動發送的郵件");
sendBtn = new JButton("發送");
sendBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
try {
send("smtp.qq.com", destInput.getText(), textInput.getText());
sendBtn.setEnabled(false);
sendBtn.setText("發送成功");
} catch (Exception e1) {
e1.printStackTrace();
errcnt++;
sendBtn.setText("發送失敗 點我重新發送 "+String.valueOf(errcnt));
}
}
});
JFrame jf = new JFrame("郵件發送");
jf.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
jf.setSize(768, 221);
jf.setLayout(new GridLayout(5, 1));
jf.add(destInputText);
jf.add(destInput);
jf.add(textInputText);
jf.add(textInput);
jf.add(sendBtn);
jf.show();
} catch(Exception e) {
e.printStackTrace();
}
}
在main函數中用兩句話即可調用圖形界面發送郵件
EmailSender sender = new EmailSender();
sender.sendInWindow();
如果要在瀏覽器處打開圖形界面發送郵件,我們同樣使用這兩句就行。我們添加一個按鈕來調出郵件窗口,並且綁定其按鈕事件。
JButton emailBtn; // 以成員變量
// ------- 其他代碼 ------//
emailBtn = new JButton("郵件發送");
emailBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
EmailSender sender = new EmailSender();
sender.sendInWindow();
}
});
完整代碼
MyHtmlBrowser.java
/*
* @Author : @李若龍 2018171028 @SZU @CSSE
*/
import java.awt.*;
import java.awt.event.*;
import java.net.*;
import java.util.*;
import java.util.regex.*;
import java.io.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.html.*;
public class MyHtmlBrowser {
String curPage = "http://www.szulrl.cn/browserTest";
JEditorPane jep; // html顯示組件
JPanel menuBox; // 上方菜單欄box 裝有很多按鈕和輸入框
JButton goBtn; // 【點我訪問】按鈕
JButton backBtn; // 回退按鈕
JButton forwBtn; // 前進按鈕
JButton refreshBtn; // 刷新按鈕
JButton favoBtn; // 收藏夾按鈕 點擊調出收藏夾
JButton emailBtn; // 發送郵件
JTextField jtf; // 輸入框
// 前進後退的棧 用雙端隊列實現
Deque<String> backQueue = new LinkedList<String>();
Deque<String> forwQueue = new LinkedList<String>();
MyHtmlBrowser() {
}
MyHtmlBrowser(String cp) {
curPage = cp;
}
/*
* @function updatePage : 將頁面設置爲當前curPage指向的頁面
*/
public void updatePage() {
jtf.setText(curPage);
try {
jep.setPage(curPage);
} catch (IOException e) {
jep.setText("<html><h1>連接錯誤或者超時!</h1><br><h1>您可以嘗試刷新頁面</h1></html>");
}
}
/*
* @function backwordPage : 回退
*/
public void backwordPage() {
if(backQueue.size()==0) return;
forwQueue.offer(new String(curPage));
curPage = backQueue.pollLast();
updatePage();
if(backQueue.size()==0) backBtn.setEnabled(false);
forwBtn.setEnabled(true);
}
/*
* @function forwordPage : 前進
*/
public void forwordPage() {
if(forwQueue.size()==0) return;
backQueue.offer(new String(curPage));
curPage = forwQueue.pollLast();
updatePage();
if(forwQueue.size()==0) forwBtn.setEnabled(false);
backBtn.setEnabled(true);
}
/*
* @function : newPage
* @param np : 新頁面的URL
*/
public void newPage(String np) {
backQueue.offer(new String(curPage));
forwQueue.clear();
curPage = new String(np);
updatePage();
backBtn.setEnabled(true);
forwBtn.setEnabled(false);
}
/*
* @function refreshPage : 刷新當前頁面
*/
public void refreshPage() {
try {
jep.setPage(curPage);
} catch (IOException e) {
jep.setText("<html><h1>連接錯誤或者超時!</h1><br><h1>您可以嘗試刷新頁面</h1></html>");
}
}
/*
* @function deleteFavorite : 刪除指定的收藏夾條目
* @param delLine : 收藏夾完整條目即 【鍵 空格 值】的字符串
*/
public void deleteFavorite(String delLine) {
try {
// 讀取 但是不讀取要刪除的行
FileInputStream is = new FileInputStream("E:/MyEclipse/WorkSpace/Hello/src/homework/favorites.txt");
BufferedReader reader = new BufferedReader(new InputStreamReader(is, "utf-8"));
String line;
LinkedList<String> lines = new LinkedList<String>();
while((line=reader.readLine())!=null) {
if(!delLine.equals(line)) {
lines.offer(line);
}
}
is.close();
// 寫入讀取的內容
FileOutputStream os = new FileOutputStream("E:/MyEclipse/WorkSpace/Hello/src/homework/favorites.txt");
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os, "utf-8"));
for(String l : lines) {
writer.write(l+"\r\n");
}
writer.flush();
writer.close();
os.close();
} catch(Exception e) {
e.printStackTrace();
}
}
/*
* @function addCurPageToFavorites : 將當前頁面添加到收藏夾
*/
public void addCurPageToFavorites() {
try {
Pattern r = Pattern.compile("(<title>)(.*?)(<)");
Matcher m = r.matcher(jep.getText());
String title = "新標籤頁";
if(m.find()) {
// System.out.println(m.group(2));
title = m.group(2);
}
// 讀取
FileInputStream is = new FileInputStream("E:/MyEclipse/WorkSpace/Hello/src/homework/favorites.txt");
BufferedReader reader = new BufferedReader(new InputStreamReader(is, "utf-8"));
String line;
LinkedList<String> lines = new LinkedList<String>();
while((line=reader.readLine())!=null) {
lines.offer(line);
}
is.close();
// 寫入讀取的內容
FileOutputStream os = new FileOutputStream("E:/MyEclipse/WorkSpace/Hello/src/homework/favorites.txt");
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os, "utf-8"));
for(String l : lines) {
writer.write(l+"\r\n");
}
writer.write(title+" "+curPage+"\r\n");
writer.flush();
writer.close();
os.close();
} catch(Exception e) {
e.printStackTrace();
}
}
/*
* @function showFavorites : 顯示當前收藏夾
* @param e : 【收藏夾】按鈕被點擊時傳入的鼠標事件MouseEvent對象
*/
public void showFavorites(MouseEvent e) throws Exception {
JPopupMenu jpm = new JPopupMenu();
JMenuItem addItem = new JMenuItem("添加當前網址到收藏夾");
addItem.setForeground(new Color(110,148,252));
addItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
addCurPageToFavorites();
//System.out.println("12313dadw");
}
});
jpm.add(addItem);
// 打開文件流讀取收藏夾本地信息
FileInputStream is = new FileInputStream("E:/MyEclipse/WorkSpace/Hello/src/homework/favorites.txt");
BufferedReader reader = new BufferedReader(new InputStreamReader(is, "utf-8"));
String line;
while((line=reader.readLine())!=null) {
// System.out.println(line);
final String key = line.split(" ")[0]; // 鍵
final String val = line.split(" ")[1]; // 值 即網址
// 創建刪除收藏按鈕並添加事件
JMenu item = new JMenu(key);
JMenuItem delItem = new JMenuItem("取消收藏");
delItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
deleteFavorite(key+" "+val);
}
});
// 創建訪問網址按鈕並添加事件
JMenuItem goItem = new JMenuItem("訪問");
goItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
newPage(val);
}
});
item.add(goItem);
item.add(delItem);
jpm.add(item);
}
jpm.show(e.getComponent(), e.getX(), e.getY());
}
/*
* @function start : 瀏覽器初始化,然後開始運作
*/
public void start() throws Exception {
jep = new JEditorPane();
jep.setEditable(false);
// 添加超鏈接點擊事件回調函數 並將JEditorPane的頁面改爲超鏈接的頁面
jep.addHyperlinkListener(new HyperlinkListener() {
public void hyperlinkUpdate(HyperlinkEvent event) {
if(event.getEventType()==HyperlinkEvent.EventType.ACTIVATED) {
// 獲取超鏈接目的地址
String hyplink = event.getURL().toString();
try {
// 判斷content type 如果是text/html就訪問 否則下載
String ctype = new URL(hyplink).openConnection().getContentType().substring(0, 4);
if(ctype.equals("text")) {
newPage(hyplink);
} else {
MultiThreadDownloader dl = new MultiThreadDownloader(
hyplink,
"E:/MyEclipse/WorkSpace/Hello/src/homework/",
"下載",
3);
dl.download();
}
} catch (Exception e) {
e.printStackTrace();
newPage(hyplink);
}
}
}
});
// 設置主頁
jep.setContentType("text/html;charset=utf-8");
try {
jep.setPage(curPage);
} catch (IOException e) {
jep.setText("<html><h1>連接錯誤或者超時!</h1><br><h1>您可以嘗試刷新頁面</h1></html>");
}
// 帶滑動條的組件 用於存放顯示html的jep組件
JScrollPane scrollpane = new JScrollPane(jep);
// 訪問按鈕 綁定訪問按鈕點擊事件 從JTextField輸入框獲取URL並且訪問
goBtn = new JButton("點我訪問網頁");
goBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// 獲取輸入的地址欄的地址
String input = jtf.getText();
if(input.equals(curPage)) {
refreshPage();
} else if(input.length()>4 && input.substring(0, 4).equals("http")){
newPage(jtf.getText());
} else {
// 跳轉到必應搜索
newPage("https://cn.bing.com/search?q=" + jtf.getText());
}
}
});
// 輸入框 輸入URL
jtf = new JTextField(40);
jtf.setText(curPage);
// 綁定回車按鍵事件
jtf.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent event) {
if(event.getKeyChar()==KeyEvent.VK_ENTER) {
goBtn.doClick(); // 按下回車等於點擊按鈕
}
}
});
backBtn = new JButton("<—後退");
backBtn.setEnabled(false);
backBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
backwordPage();
}
});
forwBtn = new JButton("前進—>");
forwBtn.setEnabled(false);
forwBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
forwordPage();
}
});
refreshBtn = new JButton("刷新頁面");
refreshBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
refreshPage();
}
});
favoBtn = new JButton("收藏夾");
favoBtn.addMouseListener(new MouseListener() {
public void mouseClicked(MouseEvent e) {
try {
showFavorites(e);
} catch (Exception e1) {
e1.printStackTrace();
}
}
// 必須實現這些接口
public void mousePressed(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
});
emailBtn = new JButton("郵件發送");
emailBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
EmailSender sender = new EmailSender();
sender.sendInWindow();
}
});
// 上方菜單盒子
JPanel menuBox = new JPanel();
menuBox.add(backBtn);
menuBox.add(forwBtn);
menuBox.add(refreshBtn);
menuBox.add(jtf);
menuBox.add(goBtn);
menuBox.add(favoBtn);
menuBox.add(emailBtn);
// 主窗體JFrame
JFrame jf = new JFrame("369危險瀏覽器");
jf.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
jf.setSize(1024,768);
// 添加組件
jf.add(menuBox, BorderLayout.NORTH);
jf.add(scrollpane, BorderLayout.CENTER);
// 添加組件
jf.show();
}
public static void main(String[] args) throws Exception {
MyHtmlBrowser browser = new MyHtmlBrowser();
browser.start();
}
}
MultiThreadDownloader.java
/*
* @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();
}
}
EmailSender.java
/*
* @Author : @李若龍 2018171028 @SZU @CSSE
*/
import java.io.*;
import java.net.*;
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import java.net.*;
import java.util.*;
import java.util.regex.*;
import java.io.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.html.*;
public class EmailSender {
JLabel destInputText;
JTextField destInput;
JLabel textInputText;
JTextField textInput;
JButton sendBtn;
int errcnt=0;
public EmailSender() {
}
public void send(String host, String dest, String text) throws Exception {
Socket socket = new Socket(host, 25);
InputStream is = socket.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(is, "utf-8"));
OutputStream os = socket.getOutputStream();
OutputStreamWriter writer = new OutputStreamWriter(os, "gbk");
writer.write("HELO a\r\n");
writer.write("auth login\r\n");
writer.write("郵箱號base64加密密文\r\n");
writer.write("郵件服務器驗證碼base64加密密文\r\n");
writer.write("MAIL FROM: 發送者的郵箱地址\r\n");
writer.write("RCPT TO: <" + dest + ">\r\n");
writer.write("DATA\r\n");
//
writer.write(text+"\r\n");
System.out.println(text);
writer.write("\r\n.\r\n");
writer.write("QUIT\r\n");
writer.flush();
/*
String line;
while((line=reader.readLine()) != null) {
System.out.println(line);
}
writer.close();
*/
}
void sendInWindow() {
try {
destInputText = new JLabel("目的郵箱地址");
destInput = new JTextField("接收者的地址");
textInputText = new JLabel("內容");
textInput = new JTextField("這是一封自動發送的郵件");
sendBtn = new JButton("發送");
sendBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
try {
send("smtp.qq.com", destInput.getText(), textInput.getText());
sendBtn.setEnabled(false);
sendBtn.setText("發送成功");
} catch (Exception e1) {
e1.printStackTrace();
errcnt++;
sendBtn.setText("發送失敗 點我重新發送 "+String.valueOf(errcnt));
}
}
});
JFrame jf = new JFrame("郵件發送");
jf.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
jf.setSize(768, 221);
jf.setLayout(new GridLayout(5, 1));
jf.add(destInputText);
jf.add(destInput);
jf.add(textInputText);
jf.add(textInput);
jf.add(sendBtn);
jf.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws Exception {
EmailSender sender = new EmailSender();
sender.sendInWindow();
}
}