我想做过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