近一個月沒敲JAVA代碼了,最近老師佈置了一個寫JAVA網盤的做業,總共花了十幾個小時,總算寫完了,debug真的累,感受本身仍是菜了,沒有那種有一個想法就能立刻用代碼實現的能力。。。。不扯了,下面開始正題。html
大概分了這麼幾個類java
原來是想作個服務器界面的,但仍是有點懶,就算了,因此這個類如今就用來建立Panserver對象git
public class MainServer { private PanServer panServer;//服務器對象 public static void main(String[] args){ MainServer ms =new MainServer(); ms.actionServer(); } // 開啓服務器 public void actionServer() { // 1.要獲得服務器狀態 if (null == panServer) { panServer = new PanServer(8888); panServer.start(); } else if (panServer.isRunning()) {// 己經在運行 panServer.stopPanServer(); panServer = null; } } }
用於創建服務器SocketServer的類github
package PanServer; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; /** * 〈服務器socketserver建立〉 * * @author ITryagain * @create 2018/12/5 * @since 1.0.0 */ public class PanServer extends Thread { private ServerSocket ss;//服務器對象 private int port;//端口 private boolean running=false;//服務器是否在運行中 PanServer(int port){ this.port=port; } public void run(){ setupServer(); } //在指定端口上啓動服務器 private void setupServer(){ try{ ss=new ServerSocket(this.port); running=true; System.out.println("服務器建立成功:"+this.port); while(running){ Socket client = ss.accept(); System.out.println("進入了一個客戶機對象:"+client.getRemoteSocketAddress()); ServerThread ct = new ServerThread(client); ct.start(); } }catch(IOException e){ // TODO Auto-generated catch block e.printStackTrace(); } } /* * 查詢服務器是否在運行 * * @return: true爲運行中 */ public boolean isRunning() { return this.running; } // 關閉服務器 public void stopPanServer() { this.running = false; try { ss.close(); } catch (Exception e) {} } }
剩下的三個類就是服務器實現的關鍵了編程
其中最重要的是ServerThread類,該類用於實現與客戶端的通訊,經過接收客戶端的指令進行不一樣的操做,其中函數以下圖所示服務器
首先,咱們在創建鏈接時,須要輸入用戶名,並建立一個文件夾,文件名爲用戶名,所以,咱們須要一個接收一開始發送的用戶名信息,寫在processSocket內機器學習
Socket sc=this.client; ins=sc.getInputStream(); ous=sc.getOutputStream(); //將輸入流ins封裝爲能夠讀取一行字符串也就是以\r\n結尾的字符串 BufferedReader brd=new BufferedReader(new InputStreamReader(ins)); sendMsg2Me("歡迎您使用!請輸入你的用戶名:"); User_name=brd.readLine(); System.out.println(User_name);
這樣咱們就讀取了用戶名,讀取用戶名後,立刻就能建立文件夾socket
File directory = new File("D:\\"+User_name); if(!directory.exists()){ directory.mkdir(); }
而後就進入while循環,不斷從客戶端讀取用戶操做信息ide
String input=brd.readLine();//一行一行的讀取客戶機發來的消息 while(true) { System.out.println("服務器收到的是"+input); if((!this.upLoad)&&(!this.downLoad)){ check(input); } if(this.upLoad){//上傳中 UpLoad(input); } if(this.downLoad){//下載中 DownLoad(input); } input=brd.readLine();//讀取下一條 System.out.println(input);
}
這裏我用了三個函數來分別處理三種狀態,其中check函數用來解碼,這裏我給出其中新建文件夾的寫法示例,刪除和重命名與之相似函數
private void check(String input){ if(input.charAt(0)=='~'){ String tmp=input.substring(input.indexOf("~")+1,input.indexOf("#")); System.out.println(tmp); if(tmp.equals("downLoad")){ this.downLoad=true; }else if(tmp.equals("upLoad")){ this.upLoad=true; }else if(tmp.equals("new")){ //新建文件夾 System.out.println(input.substring(input.indexOf("#")+1)); File directory = new File(input.substring(input.indexOf("#")+1)); if(!directory.exists()){ directory.mkdir(); } }else if(tmp.equals("delete")){ //刪除文件夾 }else if(tmp.equals("change")){ //重命名文件夾 } } }
而後剩下的就是UpLoad和DownLoad函數了,這兩個函數分別對應了上傳和下載功能,我一開始把這兩個功能都放在一開始創建的SockerServer裏面了,結果發現文件上傳了以後關閉流時把我線程也關了orz。。。仍是太菜了,這種錯都能寫出來,百度了一番,看到好多人都是再開幾個端口解決的。。。一開始就想到這方法了,但不想這麼幹,總覺的應該還有更好的辦法,可最終仍是決定用這種方法了(真香)。這裏就給出其中一個函數的寫法吧
private void UpLoad(String input){ System.out.println("上傳文件"); UpLoadThread upLoadThread = new UpLoadThread(8889,input); upLoadThread.start(); this.upLoad=false; }
既然給了UoLoad的寫法,就順便講講upLoadThread吧
/** * 〈服務器接受文件線程〉 * * @author ITryagain * @create 2018/12/8 * @since 1.0.0 */ public class UpLoadThread extends Thread{ private ServerSocket UpLoadServer; private int port; private String input; private FileOutputStream fos; UpLoadThread(int port,String input){ this.port=port; this.input=input; } public void run(){ } private static DecimalFormat df = null; static { // 設置數字格式,保留一位有效小數 df = new DecimalFormat("#0.0"); df.setRoundingMode(RoundingMode.HALF_UP); df.setMinimumFractionDigits(1); df.setMaximumFractionDigits(1); } /** * 格式化文件大小 * @param length * @return */ private String getFormatFileSize(long length) { } }
大體函數就是這樣的,其中run方法裏面就是文件接收了,(若是發現缺了什麼本身補一補,就一個變量的申明沒加上去)
try{ UpLoadServer = new ServerSocket(port); socket = UpLoadServer.accept(); dis = new DataInputStream(socket.getInputStream()); //文件名和長度 String fileName = input.substring(input.indexOf("#")+1); long fileLength = dis.readLong(); File file = new File(fileName); fos = new FileOutputStream(file); //開始接收文件 byte[] bytes = new byte[1024]; int length=0; while((length = dis.read(bytes, 0, bytes.length)) != -1) { fos.write(bytes, 0, length); fos.flush(); } System.out.println("======== 文件接收成功 [File Name:" + fileName + "] [Size:" + getFormatFileSize(fileLength) + "] ========"); try { if(fos!=null) fos.close(); if(dis != null) dis.close(); if(socket !=null) socket.close(); if(UpLoadServer!=null) UpLoadServer.close(); } catch (Exception e) {} }catch(IOException e){ }
而後就是 getFormatFileSize() 函數了,這個函數是用來幹嗎的呢?就是用來轉化一下文件大小單位的,否則到時候一個幾 GB 的文件顯示的就是 *****
******B了,那麼長一串,看着也不舒服。
private String getFormatFileSize(long length) { double size = ((double) length) / (1 << 30); if(size >= 1) { return df.format(size) + "GB"; } size = ((double) length) / (1 << 20); if(size >= 1) { return df.format(size) + "MB"; } size = ((double) length) / (1 << 10); if(size >= 1) { return df.format(size) + "KB"; } return length + "B"; }
服務器端剩下沒講的代碼其實都差很少,就本身去實現吧
客戶端就比較麻煩了,尤爲是這個界面,花了我老半天時間
這個類就是客戶端用於建立界面以及管理線程的類了,界面我是用JTree來實現的,看下函數先吧
public class MainClient extends JFrame implements ActionListener, TreeModelListener { private JTree tree; private int ServerIP = 8888; private JLabel statusLabel; private DefaultTreeModel treeModel; private String oldNodeName; private OutputStream ous; private Socket client; private String name; private String stress = "D:\\"; private String downLoadStress="D:\\下載\\"; public static void main(String[] args){ MainClient mc=new MainClient(); mc.showLoginUI(); } public void showLoginUI(){ } // 登錄事件處理 private void loginAction() { } //顯示操做窗口 private void showMainUI() { } @Override public void actionPerformed(ActionEvent e) { if (e.getActionCommand().equals("新建文件夾")) { } if (e.getActionCommand().equals("刪除文件夾")) { } if (e.getActionCommand().equals("上傳文件")) { } if(e.getActionCommand().equals("下載文件")){ } } @Override /* * 修改文件名字 */ public void treeNodesChanged(TreeModelEvent e) { } //選擇上傳文件 public void chooseSendFile(){ } public void sendFile(File file,String path) throws IOException{ } private String getSendPath(TreePath parentPath){ } //選擇下載文件 public void chooseDownLoadFile() { } //下載文件 public void downloadFile(String path) throws IOException{ } private String getDownLoadPath(TreePath parentPath){ } private void newFile(String path){ } private void deleteFile(String path){ } private void ChangeFileName(String NewName,String path){ } @Override public void treeNodesInserted(TreeModelEvent e) { } @Override public void treeNodesRemoved(TreeModelEvent e) { } @Override public void treeStructureChanged(TreeModelEvent e) { } }
先來看看界面吧,打開後你可能只能看到三個按鈕,拉動一下框框就能看到第四個了,大小設置有點問題。
private void showMainUI() { JFrame frame=new JFrame("網盤"); Container contentPane = frame.getContentPane(); DefaultMutableTreeNode root = new DefaultMutableTreeNode(name); tree = new JTree(root); tree.addMouseListener(new MyTreeMouseListener(oldNodeName)); treeModel = (DefaultTreeModel)tree.getModel(); treeModel.addTreeModelListener(this); tree.setEditable(true); tree.getCellEditor().addCellEditorListener(new MyTreeCellEditorListener(tree)); JScrollPane scrollPane = new JScrollPane(); scrollPane.setViewportView(tree); JPanel toolBarPanel = new JPanel(); JButton b = new JButton("新建文件夾"); b.addActionListener(this); toolBarPanel.add(b); b = new JButton("刪除文件夾"); b.addActionListener(this); toolBarPanel.add(b); b = new JButton("上傳文件"); b.addActionListener(this); toolBarPanel.add(b); b = new JButton("下載文件"); b.addActionListener(this); toolBarPanel.add(b); statusLabel = new JLabel("Action"); contentPane.add(toolBarPanel, BorderLayout.NORTH); contentPane.add(scrollPane, BorderLayout.CENTER); contentPane.add(statusLabel, BorderLayout.SOUTH); frame.pack(); frame.setVisible(true); frame.requestFocus(); frame.setSize(400, 400); frame.setLocationRelativeTo(null); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); tree.setRootVisible(false); }
裏面兩監聽類的實現(提醒一下,可能到後面你作重命名文件夾會遇到坑,跟其中一個監聽類有關,提醒一下oldNodeName是用來記錄修改前的文件夾名稱的,想辦法獲取這個變量的值就能很快實現文件夾重命名的功能),還有文件路徑的實現也本身好好想一想
至於界面的其它一些功能實現能夠去看這篇博客(包括兩個監聽類的實現以及 actionPerformed() 和 treeNodesChanged() 等函數的實現)http://www.javashuo.com/article/p-gkueliis-co.html
而後這裏給出NewFile()函數寫法
private void newFile(String path){ String fileName = "~new#"+path+"\r\n"; System.out.println(fileName); try { this.ous.write(fileName.getBytes()); this.ous.flush(); } catch (IOException e) { e.printStackTrace(); } }
至於 delete 和 change 的寫法與此相似
前面我給出了服務器上傳文件的類,這裏就給出客戶端上傳文件的類的寫法
先看下 sendFile() 函數
public void sendFile(File file,String path) throws IOException{ String fileName = "~upLoad#"+path+"\\"+file.getName()+"\r\n"; System.out.println(fileName); this.ous.write(fileName.getBytes()); this.ous.flush(); FileSendThread send_socket = new FileSendThread(8889,file,path); send_socket.start(); }
客戶端上傳文件的類
public class FileSendThread extends Thread { private Socket fileSendSocket; private int port; private String path; private File file; private OutputStream ous; private FileInputStream fis; private DataOutputStream dos; FileSendThread(int port, File file,String path){ this.port=port; this.file=file; this.path=path; } public void run(){ try{ fileSendSocket = new Socket("localhost",this.port); // 發送: 文件名稱 文件長度 this.ous = fileSendSocket.getOutputStream(); dos = new DataOutputStream(this.ous); dos.writeLong(file.length()); //開始傳輸文件 System.out.println("======開始傳輸文件======="); byte[] bytes = new byte[1024]; int length; long progress = 0; fis = new FileInputStream(file); while((length=fis.read(bytes,0,bytes.length))!=-1){ dos.write(bytes,0,length); dos.flush(); progress = progress + length; System.out.print("| " + (100*progress/file.length()) + "% |"); } System.out.println(); System.out.println("======== 文件傳輸成功 ========"); }catch(IOException e1){ }finally { try { if (fis != null) fis.close(); if (dos != null) dos.close(); }catch(IOException e){ // TODO Auto-generated catch block e.printStackTrace(); } } } }
至於下載功能,與上傳相似,把服務器和客戶端上傳類中的部分代碼換一換就行了。
到了這裏大家是否還記得咱們在通訊鏈接創建時須要傳輸用戶名?實現方式以下
登錄框的實現,我這裏算是偷懶了,直接利用了 JOptionPane
public void showLoginUI(){ name = JOptionPane.showInputDialog("請輸入用戶名"); System.out.println(name); loginAction(); }
// 登錄事件處理 private void loginAction() { try { this.client = new Socket("localhost",ServerIP); if(loginServer()){ showMainUI(); }else{ JOptionPane.showMessageDialog(null,"登錄失敗","肯定",JOptionPane.WARNING_MESSAGE); } } catch (IOException e) { JOptionPane.showMessageDialog(null,"鏈接失敗","肯定",JOptionPane.WARNING_MESSAGE); } }
private boolean loginServer(){ try{ this.ous = this.client.getOutputStream(); String _name=name+"\r\n"; this.ous.write(_name.getBytes()); this.ous.flush(); return true; }catch(IOException e){ return false; } }
順便介紹幾個方法
文件夾重命名
File directory = new File("D:\a.txt"); directory.renameTo(new File("D:\b.txt"));
這裏實現的是將a.txt重命名爲b.txt,對文件夾也有效
文件夾刪除
boolean success = (new File("D:\a.txt").delete(); if(success){ System.out.println("刪除成功"); }else{ ystem.out.println("刪除失敗"); }
還有String的幾個操做
一、個人編碼
2.String中的 subString() 和 indexOf() 函數
到這裏,個人網盤介紹算是完了,由於這是做業而且還沒檢查的緣故,就沒有把全部代碼放出來了,就介紹了下思路,順便給將要寫網盤或者正在寫網盤的大家一點思路,相信有了這些思路,大家能很快地寫完網盤,或者有了寫的思路。過久沒寫代碼果真不行,搞了一個多月的強化學習、機器學習和數據挖掘,差點就不會寫代碼了(面向搜索引擎編程感受還行。。。百度有時很強大,可能只是你沒學會正確的搜索的姿式)
檢查完了,開源初版代碼https://github.com/leo6033/JAVA-Pan