我想做過web開發的程序員大部分都做過文件上傳的功能,大多數時候我們都是借助于commons-fileupload這樣的jar包實現的。下面我試著通過讀取Socket的輸入流來實現一個文件上傳的功能。
在做文件上傳之前我們需要先了解一下HTTP POST的附件上傳協議。HTTP附件上傳協議是RFC1876協議,RFC1876協議是在HTTP協議的基礎上為INPUT標簽增加了file屬性,同時限定了Form的method必須為POST
,ENCTYPE
必須為multipart/form-data
。RFC1867協議對HTTP頭作了適當地變更,content-type頭由以前的:content-type:application/x-www-form-urlencoded變為content-type:multipart/form-data;+空格+boundary=字符串
。RFC1867增加了文件上傳得功能,而上傳文件內容自然也會被加入到HTTP的實體中。現在因為既有HTTP一般的參數實體,又有上傳文件的實體,所以用boundary把每種實體進行了分割。具體的看下圖:
接下來就開始我們的代碼部分吧。
我在前面的文章中寫過創建一個自己的Web服務器,現在我們的重點要放在對socket的輸入流的解析中。具體代碼如下:
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
|
public void parseRequest() { LineNumberReader br = new LineNumberReader( new InputStreamReader(inputStream)); StringBuffer sb = new StringBuffer(); String str = null ; try { //讀取請求行 String requestLine = br.readLine(); if (!StringUtils.isEmpty(requestLine)) { sb.append(requestLine); String[] reqs = requestLine.split( " " ); if (reqs != null && reqs.length > 0 ) { if ( "GET" .equals(reqs[ 0 ])) { method = "GET" ; } else { method = "POST" ; } } } //讀取請求頭 while ((str = br.readLine()) != null ) { if ( "" .equals(str)) { break ; } if (!StringUtils.isEmpty(str)) { if (str.indexOf( ":" ) > 0 ) { String[] strs = str.split( ":" ); headers.put(strs[ 0 ].toLowerCase(), strs[ 1 ].trim()); } } sb.append(str).append( "\n" ); } //POST請求,Content-type為 multipart/form-data String contentType = null ; if ( "POST" .equals(method) && ((contentType = headers.get( "content-type" )) != null && headers.get( "content-type" ).startsWith( "multipart/form-data" ))) { //文件上傳的分割位 這里只處理單個文件的上傳 String boundary = contentType.substring(contentType.indexOf( "boundary" ) + "boundary=" .length()); //解析消息體 while ((str = br.readLine()) != null ) { //解析結束的標記 do { //讀取boundary中的內容 //讀取Content-Disposition str = br.readLine(); //說明是文件上傳 if (str.indexOf( "Content-Disposition:" ) >= 0 && str.indexOf( "filename" ) > 0 ) { str = str.substring( "Content-Disposition:" .length()); String[] strs = str.split( ";" ); String fileName = strs[strs.length - 1 ].replace( "\"" , "" ).split( "=" )[ 1 ]; System.out.println( "fileName = " + fileName); //這一行是Content-Type br.readLine(); //這一行是換行 br.readLine(); //正式去讀文件的內容 BufferedWriter bw = null ; try { bw = new BufferedWriter( new OutputStreamWriter( new FileOutputStream( "G:\\LearnVideo\\fileLoad" + File.separator + fileName), "UTF-8" )); while ( true ) { str = br.readLine(); if (str.startsWith( "--" + boundary)) { break ; } bw.write(str); bw.newLine(); } bw.flush(); } catch (Exception e) { } finally { if (bw != null ) { bw.close(); } } } if (str.indexOf( "Content-Disposition:" ) >= 0 ) { str = str.substring( "Content-Disposition:" .length()); String[] strs = str.split( ";" ); String name = strs[strs.length - 1 ].replace( "\"" , "" ).split( "=" )[ 1 ]; br.readLine(); StringBuilder stringBuilder = new StringBuilder(); while ( true ) { str = br.readLine(); if (str.startsWith( "--" + boundary)) { break ; } stringBuilder.append(str); } parameters.put(name, stringBuilder.toString()); } } while (( "--" + boundary).equals(str)); //解析結束 if (str.equals( "--" + boundary + "--" )) { break ; } } } //System.out.println(sb.toString()); //獲取URI uri = StringUtils.parserUri(sb.toString(), " " ); int flag = - 1 ; //說明有參數 if ((flag = uri.indexOf( '?' )) >= 0 ) { String oldUri = uri; uri = uri.substring( 0 ,flag); String parameterPath = oldUri.substring(flag+ 1 ); String[] parameter = parameterPath.split( "&" ); if (parameter != null && parameter.length > 0 ) { for ( int i = 0 ; i < parameter.length; i++) { String str1 = parameter[i]; if ((flag = str1.indexOf( '=' )) >= 0 ){ String key = str1.substring( 0 ,flag); String value = str1.substring(flag+ 1 ); parameters.put(key,value); } else { parameters.put(str, null ); } } } } } catch (IOException e) { e.printStackTrace(); } } |
我們啟動自己創建的Web服務器,然后在瀏覽器中輸入:http://localhost:8004/static/uploadPage.html,頁面如下:
選擇我們要上次的文件,然后點擊上傳按鈕,我們會發現我們的功能已經被上傳到G:\LearnVideo\fileLoad這個目錄下了。示例如下:
完整的代碼請從這里下載:FullStackTraining.rar
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。
原文鏈接:http://blog.csdn.net/zknxx/article/details/60884573