客戶端設計思路:客戶端與服務端建立連接,選擇客戶端本地文件,先將文件名及大小等屬性發送給服務端,再將文件通過流的方式傳輸給服務端。傳輸的進度打印到控制臺中,直到傳輸完成。
服務端設計思路:服務端接收客戶端的請求(阻塞式),每接收到一個客戶端請求連接后,就新開一個處理文件的線程,開始寫入流,將文件到服務器的指定目錄下,并與傳輸過來的文件同名。
下面是客戶端和服務端的代碼實現:
客戶端代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
|
import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.net.Socket; /** * 文件傳輸Client端<br> * 功能說明: * * @author 大智若愚的小懂 * @Date 2016年09月01日 * @version 1.0 */ public class FileTransferClient extends Socket { private static final String SERVER_IP = "127.0.0.1" ; // 服務端IP private static final int SERVER_PORT = 8899 ; // 服務端端口 private Socket client; private FileInputStream fis; private DataOutputStream dos; /** * 構造函數<br/> * 與服務器建立連接 * @throws Exception */ public FileTransferClient() throws Exception { super (SERVER_IP, SERVER_PORT); this .client = this ; System.out.println( "Cliect[port:" + client.getLocalPort() + "] 成功連接服務端" ); } /** * 向服務端傳輸文件 * @throws Exception */ public void sendFile() throws Exception { try { File file = new File( "E:\\JDK1.6中文參考手冊(JDK_API_1_6_zh_CN).CHM" ); if (file.exists()) { fis = new FileInputStream(file); dos = new DataOutputStream(client.getOutputStream()); // 文件名和長度 dos.writeUTF(file.getName()); dos.flush(); dos.writeLong(file.length()); dos.flush(); // 開始傳輸文件 System.out.println( "======== 開始傳輸文件 ========" ); byte [] bytes = new byte [ 1024 ]; int length = 0 ; long progress = 0 ; while ((length = fis.read(bytes, 0 , bytes.length)) != - 1 ) { dos.write(bytes, 0 , length); dos.flush(); progress += length; System.out.print( "| " + ( 100 *progress/file.length()) + "% |" ); } System.out.println(); System.out.println( "======== 文件傳輸成功 ========" ); } } catch (Exception e) { e.printStackTrace(); } finally { if (fis != null ) fis.close(); if (dos != null ) dos.close(); client.close(); } } /** * 入口 * @param args */ public static void main(String[] args) { try { FileTransferClient client = new FileTransferClient(); // 啟動客戶端連接 client.sendFile(); // 傳輸文件 } catch (Exception e) { e.printStackTrace(); } } } |
服務端代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
|
import java.io.DataInputStream; import java.io.File; import java.io.FileOutputStream; import java.math.RoundingMode; import java.net.ServerSocket; import java.net.Socket; import java.text.DecimalFormat; /** * 文件傳輸Server端<br> * 功能說明: * * @author 大智若愚的小懂 * @Date 2016年09月01日 * @version 1.0 */ public class FileTransferServer extends ServerSocket { private static final int SERVER_PORT = 8899 ; // 服務端端口 private static DecimalFormat df = null ; static { // 設置數字格式,保留一位有效小數 df = new DecimalFormat( "#0.0" ); df.setRoundingMode(RoundingMode.HALF_UP); df.setMinimumFractionDigits( 1 ); df.setMaximumFractionDigits( 1 ); } public FileTransferServer() throws Exception { super (SERVER_PORT); } /** * 使用線程處理每個客戶端傳輸的文件 * @throws Exception */ public void load() throws Exception { while ( true ) { // server嘗試接收其他Socket的連接請求,server的accept方法是阻塞式的 Socket socket = this .accept(); /** * 我們的服務端處理客戶端的連接請求是同步進行的, 每次接收到來自客戶端的連接請求后, * 都要先跟當前的客戶端通信完之后才能再處理下一個連接請求。 這在并發比較多的情況下會嚴重影響程序的性能, * 為此,我們可以把它改為如下這種異步處理與客戶端通信的方式 */ // 每接收到一個Socket就建立一個新的線程來處理它 new Thread( new Task(socket)).start(); } } /** * 處理客戶端傳輸過來的文件線程類 */ class Task implements Runnable { private Socket socket; private DataInputStream dis; private FileOutputStream fos; public Task(Socket socket) { this .socket = socket; } @Override public void run() { try { dis = new DataInputStream(socket.getInputStream()); // 文件名和長度 String fileName = dis.readUTF(); long fileLength = dis.readLong(); File directory = new File( "D:\\FTCache" ); if (!directory.exists()) { directory.mkdir(); } File file = new File(directory.getAbsolutePath() + File.separatorChar + 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) + "] ========" ); } catch (Exception e) { e.printStackTrace(); } finally { try { if (fos != null ) fos.close(); if (dis != null ) dis.close(); socket.close(); } catch (Exception e) {} } } } /** * 格式化文件大小 * @param length * @return */ 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" ; } /** * 入口 * @param args */ public static void main(String[] args) { try { FileTransferServer server = new FileTransferServer(); // 啟動服務端 server.load(); } catch (Exception e) { e.printStackTrace(); } } } |
測試的結果(客戶端):
測試的結果(服務端):
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。
原文鏈接:http://blog.csdn.net/huang930528/article/details/52401565