在计算机网络中,如果两台机器要通信,他们首先要定义通信数据的格式,这样在服务器收到客户端的请求消息时,它才能正确的解析请求的内容,然后根据请求内容处理逻辑,并将相应消息传递会客户端;此时,客户端也要根据已定义的响应数据格式解析响应消息。在浏览器和HTTP服务器之间的通信数据格式使用HTTP协议定义。请求消息
其中请求消息的格式为:例子:POST /index.jsp?articleId=1234&articleName=GoodArticle HTTP/1.1
Host: www.blogjava.com
Content-Length: 74
Content-Type: application/x-www-form-urlencoded
myText1=hello+world&myText2=%E4%BD%A0%E5%A5%BD%E4%B8%96%E7%95%8C
请求消息由三部分组成:请求行、消息报头、请求正文。请求行格式为:请求方法、空格、URL、版本号、回车、换行。请求方法集:GET、POST、HEAD、PUT、DELETE、TRACE、CONNECT、OPTIONS。URL中'?'之后的值用于表达请求参数。版本号可以是:HTTP/1.0、HTTP/1.1。消息报头格式为:报头名字、冒号(':')、空格、报头值、回车、换行。消息报头用于传递元数据信息,用于表达消息正文的类型、编码格式、缓存等,以回车换行结束。当遇到一个空行(只有回车换行),表示消息报头结束。消息正文,可以是任意定义的格式,它接在消息报头后(空行之后)。请求消息是否包含消息体由Content-Length或Transfer-Encoding决定,如果规范定义的请求方法不允许包含消息体,则在请求消息中不可以包含消息体。Server在解析时,如果请求方法不支持消息体,则在请求消息中包含的消息体会被忽略。请求方法:
HTTP1.1定义的请求方法有
OPTIONS:
请求查询服务器的性能,或查询与资源相关的选项和需求。
该方法的Response不可缓存。当前版本HTTP不支持该请求方法包含消息实体。如果该请求包含请求消息体,HTTP服务器将会抛弃这些信息。如果Request-URI为*,该请求类似“ping”或“no-op”操作。
GET:
请求获取Request-URI所标识的资源。
响应消息可缓存。在GET请求消息中,如果包含If-Modified-Since、If-Unmodified-Since、If-Match、If-None-Match、If-Range字段,则该请求称为“条件GET”。
HEAD:
请求获取由Request-URI所标识的资源的响应消息报头。
响应消息可缓存。
POST:
在Request-URI所标识的资源后附加新的数据。
如果在HTTP服务器中有创建新的资源,则该方法的响应状态码必须是201(Created),并且响应消息实体包含描述请求的状态,以及一个Location响应消息头。该方法的响应消息不可被缓存。
PUT:
请求服务器存储一个资源,并用Request-URI作为其标识。
如果Request-URI指向一个已存在的资源,那么包含的消息实体被视为已存在资源的新版本,如果Request-URI没有对应的资源,则HTTP服务器可以通过该URI创建相应资源,此时HTTP服务器响应201(Created)状态码。如果修改了已存在的资源,则返回200(OK)或204(No Content)。HTTP服务器不可以忽略Content-*消息头(如Content-Range),如果HTTP服务器不能理解该消息头,则返回501(Not Implemented)响应消息码。
DELETE:
请求服务器删除Request-URI所标识的资源。
即使该方法的返回状态码表明该操作以执行成功,客户端还是不能保证该方法需要删除的操作已经被执行了。但是HTTP服务器必须保证在返回响应给客户端的时候,HTTP服务器已经打算删除这个资源或把它移动到一个不可访问的位置。成功的响应码为200(OK),并且响应消息实体中可以包含一些描述信息;202(Accept)表明这个操作还没被完全执行;204(No Content)表示这个操作已经执行完成,但是没有响应消息实体。该方法的响应消息不可被缓存。
TRACE:
请求服务器会送收到的请求信息,主要用于测试或诊断。
该方法以200返回标识成功。该请求消息不可包含请求消息实体。该方法的Response必须在响应消息实体中包含所有的请求消息,其相应消息的Content-Type值为:message/http,该Response不可被缓存。
CONNECT:
保留将来使用。
扩展的方法:
用户自定义扩展方法。
如果Server能识别某个请求方法但是不允许该请求方法,则应该返回405(Method Not Allowed)响应状态。如果Server无法识别某个请求方法或者当前Server没有实现这个请求方法,则应该返回501(Not Implemented)状态码。
Request-URI支持的值有:*|absoluteURI|abs_path|authority
-
*表示请求不应用于某个特定的资源,并且只对于某些不需要应用于特定资源的请求方法,如:OPTIONS * HTTP/1.1
-
absoluteURI:当客户端是向一个代理发送请求时需要使用absoluteURI,然后这个代理会转发这个请求,并返回响应。虽然按规范,HTTP1.1客户端只发送absoluteURI到代理服务器,但是为了在将来的HTTP版本中可以允许请求都转换成absoluteURI,所有HTTP1.1 Server必须可以解析absoluteURI风格的请求:GET http://www.w3.org/pub/WWW/TheProject.html HTTP/1.1
-
authority只在CONNECT请求方法中使用。
-
abs_path:用于表示Server的资源,而Server本身的信息在Host消息头中表示:
GET /put/www/TheProject.html HTTP/1.1
Host: www.w3.org
Resource Identification rules:
- 如果Request-URI是absoluteURI,并且这个absolute的host和server的host相同,则忽略Host头。
- 如果Request-URI不是absoluteURI,并且请求消息包含Host头,host由Host消息头决定。
- 如果1或2中的host不是一个合法的host,则返回400(Bad Request)响应消息。
响应消息
响应消息的格式定义为:例子:HTTP/1.1 200 OK //请求成功
Server: Microsoft-IIS/5.0 //web服务器
Date: Thu,08 Mar 200707:17:51 GMT
Connection: Keep-Alive
Content-Length: 23330
Content-Type: text/html
Expries: Thu,08 Mar 2007 07:16:51 GMT
Set-COOKIE: ASPSESSIOnIDQAQBQQQB=BEJCDGKADEDJKLKKAJEOIMMH; path=/
Cache-control: private
响应消息也有三部分组成:状态行、消息报头、响应正文。状态行格式:版本号、空格、状态、空格、状态短语、回车、换行。版本号可以是:HTTP/1.0、HTTP/1.1。状态号和状态短语由HTTP协议定义,状态号有5中取值可能:1xx:指示信息--表示请求已经接收,继续处理。2xx:成功--表示请求已经被成功接收、理解、处理。3xx:重定向--要完成请求,必须进行更进一步操作。4xx:客户端错误--请求有语法错误或请求无法实现。5xx:服务器端错误--服务器未能实现合法的请求。常见的状态号和状态短语有:200 OK --请求成功。304 Not Modified --资源没有改变。400 Bad Request --客户端请求有语法错误,不能被服务器理解。401 Unauthorized --请求未经授权(和WWW-Authenticate报头一起使用)。403 Forbidden --服务器收到请求,但是拒绝提供服务。404 Not Found --请求资源不存在。500 Internal Server Error --服务器发生不可预期的错误。503 Server Unavailable --服务器当前不能处理客户端的请求,一段时间后可能恢复正常。响应报头和请求报头格式一样:报头名、冒号(':')、空格、报头值、回车、换行。用于记录响应消息的元数据,表达响应消息的长度、编码方式、COOKIEe等信息。遇到一个空行(只有回车换行)表示响应消息报头结束。响应消息正文紧随响应消息报头(在空行后),它可以是任意的内容,由客户端解析。在响应消息中是否包含消息体是由请求方法和响应状态码决定,所有对HEAD请求方法的响应消息不能包含任何消息体,即使在响应消息中可能会包含实体消息头,以至于有人会认为这个响应消息包含消息体。所有1XX(informational)、204(no content)、304(not modified)响应消息不能包含消息体。所有其他的响应消息都包含消息体,即使有些时候消息体的长度是0。杂记
协议本身,最终要的在于消息格式,HTTP协议的请求消息和响应消息已经详细说明了,剩下的就是一些具体细节的问题,比如URI的格式、各种消息报头代表的含义、响应状态号对应的含义等。因为时间有限,不做整理,所以只是一些阅读协议的杂记。URI(Uniform Resource Identifiers),又名:UDI(Universal Document Identifiers),是URL(Uniform Resource Locators)和URN(Unifrom Resource Names)的组合。从HTTP协议的角度,URL只是一个由字符串组成的用于名称、位置等的标识符。在HTTP协议中使用URL作为定位符,它的格式为:http://${host}[:${port}][${abs_path}[?${query}]]Date/Time格式:因为历史原因,HTTP支持三种日期、时间格式:Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsolted by RFC 1036Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format其中第一种格式是推荐的网络格式,而且它是固定长度的。HTTP1.1客户端和服务器端需要能接收所有以上三种日期格式,但是只生成第一种日期格式。所有HTTP日期、时间都必须是格林威治时间(GMT,Greenwich Mean Time),在HTTP中,GMT和UTC(Coordinated Universal Time)时间相同。编码集:同MINE格式规范定义。在Cotent-Type头中定义。内容编码:主要用于对消息实体是否压缩、采用什么压缩算法的表示。在HTTP1.1中使用Accept-Encoding和Content-Encoding头中定义,支持的值有:gzip、compress(废弃)、deflate(zlib格式)、identity(默认不压缩,只能用于Accept-Encoding中,不能用于Content-Encoding)。这些支持的格式在IANA(Internet Assigned Numbers Authority)中注册。传输编码(transfer-coding):用表示可以、需要应用到实体主体以确保通过网络“安全传输”的编码转换。这与内容编码不同,传输编码是消息而非原始实体的属性。所有传输编码值大小写无关,它类似于MINE编码中的Content-Transfer-Encoding。可用的值为:chunked,identity,gzip,compress,deflat。HTTP/1.0不支持。媒体类型:HTTP通过Content-Type和Accept头部域以提供可扩展的数据类型。值格式:${type}/${subtype};${paramName}=${paramValue};....Product符号:用于允许通信应用程序通过软件名称和版本号来标识它自己,比如:User-Agent: CERN-LineMode/2.15 libwww/2.17b3Server: Apache/0.84qvalue:使用[0-1]的值来表达参数的重要性,0表示不可接受,该值的小数部分不可操作三位。语言标签:用于表达消息实体的自然语言,用Accept-Language和Content-Language字段表达。它的值可以是:en、en-US、en-cockney、i-cherokee、x-pig-latin等。实体标签:用于比较相同请求资源的两个或多个实体的比较。如If-Match、If-None-Match、If-Range等头部域名。范围标签:HTTP/1.1允许客户端值请求响应实体的某部分(范围)作为响应消息,如Range、Content-Range头部域,他们的单位在HTTP/1.1中只支持byte。消息报头详解
在请求消息和响应消息中都有消息报头,消息报头在HTTP1.1协议中(RFC2616)有三种类型的头:通用头(General Header)、请求头(Request Header)、响应头(Response Header)、实体头(Entity Header)。其格式为:header-name: header-value。其中header-name大小写无关,以一个空行(只包含回车和换行)结束。header-value可以以任意数量的LWS开头(一般是一个空格)。消息头可以以至少一个SP或HT开头的方式扩展成多行(原文:header fields can be extended over multiple lines by preceding each extra line with at least one SP or HT,感觉理解的有问题....)。相同的header-name可以重复出现。通用消息头:
Cache-Control:
指定缓存指令。如请求相关的指令:no-cache、no-store、max-age、max-stale、min-fresh、no-transform、only-if-cached、cache-extension,响应相关的指令:public、private、no-cache、no-store、no-transform、must-revalidate、proxy-revalidate、max-age、s-maxage、cache-extension。
Connection:
客户端通过发送包含close值的Connection头,表达在这次请求结束后,Server可以关闭这个连接,此时Server如果选择发送响应后关闭连接,则在响应消息中需要包含值为close的Connection头。
允许发送者指定当前Connection的一些选项。HTTP/1.1只定义了close的值,表示响应返回后,当前Connection将会被关闭。
Date:
表示消息发送的时间,你的描述格式由RFC822定义。例如Mon, 31 Dec 2001 04:25:57GMT
Pragma:
用于包含实现相关的指令。如no-cache
Trailer:
表示指定的头在chunked消息的尾部。
Transfer-Encoding:
消息在传输时使用的编码。如chunked。
Upgrade:
允许客户端指定它额外支持的传输协议,如果服务器发现更新的传输协议更合适当前请求,则它可以将当前传输协议转换成更新的传输协议。如HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11等。
Via:
用于网关或代理服务器,以指示客户端和服务器之间的中间协议和接收者。
Warning:
用于添加一些额外的状态或转换信息。
请求消息头:
请求消息头允许客户端传递一些额外关乎客户端信息给Server,这些字段类似在方法调用中的参数。
Accept:
指定当前请求响应可以接受的媒体类型,以逗号间隔。如:“audio/*; q=0.2, audio/basic, text/html, */*”。
Accept-Charset:
指定当前请求响应可以接受的字符编码集,以逗号间隔。如“iso-8859-5, Unicode-1-1; q=0.8”。
Accept-Encoding:
类似Accept,定义消息实体的编码方式。如“compress, gzip, *, identity; q=0.5”等。
Accept-Language:
类似Accept,定义自然语言的限制。如“da, en-gb; q=0.8, en; q=0.7”等。
Authorization:
客户端向服务器传递认证信息。
Expect:
客户端发送一个包含100-continue值的Expect字段头,以在不发送真正消息实体的情况下测试服务器是否能接收这个消息。此时Server响应417(Expectation Failed)或100(Continue),然后客户端决定是否要继续发送请求消息体。
请求消息头,用于指定客户端对服务器端响应行为的需求。如100-continue、102-processing等。
From:
请求头,指定用户的email地址。
Host:
请求头,指定服务器的主机名和端口。如:www.w3.org:8080
If-Match:
请求头,用于条件请求方法。
If-Modified-Since:
请求头,用于条件请求方法:请求变体自从指定的时间内没有发生改变。
If-None-Match:
请求头,用于条件请求方法。
If-Range:
表示如果实体没有变法,则发送给客户端指定部分的实体。
If-Unmodified-Since:
如果实体在指定时间内没有发生变化,则直接发送响应,否则返回412(Precondition Failed)的响应。
Max-Forwards:
请求头,指定最大可以被代理、网关服务器转发的次数。
Proxy-Authorization:
请求头,用于客户端包含对代理服务器的认证信息。
Range:
指示范围,如bytes=0-499
Referer:
请求头,允许客户端指定当前URI是从哪个URI中获得的。
TE:
请求头,用于指示在响应中希望接收的扩展传输编码。如deflate、trailers, deflate;q=0.5等。
User-Agent:
请求头,用于添加客户端软件信息。
响应消息头
响应消息头允许Server传递一些关于响应的额外信息给客户端。
Accept-Ranges:
响应头,允许服务器指定它可接受的请求范围。如“bytes”、“none”等。
Age:
响应头,当代理服务器用自己缓存的实体去响应请求时,该头部表示该实体从产生到现在经过多少时间了。该数值的代为秒。
ETag:
响应消息头,用于指定请求变体中的实体标签的当前值。如xyzzy,W/xyzzy等。
Location:
响应头,用于指示接收方重定向。
Proxy-Authenticate:
响应头,在407(Proxy Authenticate Required)响应中,它包含代理服务器需要的验证模式和参数。
Retry-After:
响应头,通503(Service Unavailable)响应一起使用,用于指定服务器预计不可用时间;或者3xx,用于指定客户端在重定向之前等待的时间。
Server:
响应头,用于添加服务器软件信息。
Vary:
用于指示用于决定当响应是最新时,是否cache可以用于接下来的响应并且不用验证的请求字段集合。
WWW-Authenticate:
响应头,用于401(Unauthorized)响应消息中,用于指定服务器需要的认证模式和参数。
实体消息头
实体消息头属于实体的一部分,是实体的元数据。
Allow:
实体头,列出所有对当前Request-URI指定资源支持的方法。
Content-Encoding:
实体头,用于指定实体内容的编码方式。如gzip、identity等。
Content-Language:
实体头,用于定义实体内容的自然语言。如da、en、mi等。
Content-Length:
实体头,用于指定实体内容的长度。
Content-Location:
实体头,用于指定资源所在的URI。
Content-MD5:
实体头,用于表示实体内容的数字摘要。
Content-Range:
实体头,用于指定实体内容的范围。如bytes 0-499/1234(即单位 范围/总长度)。
Content-Type:
实体头,用于指定实体内容的媒体类型。如text/html; charset=ISO-8859-4等。
Expires:
实体头,用于响应在多少时间后在Cache中失效。
Last-Modified:
实体头,用于指定服务器认为当前变体的修改时间。
响应状态码详解
1xx: Informational - Request received, continuing process
这是一个临时性的响应,在HTTP/1.0协议中不存在,因而不可以向HTTP/1.0的客户端发送该状态码响应。客户端必须在获得正常响应之前能接收一个或多个1xx响应,即使它并没有预计会收到1xx响应。该响应码只有状态行和可选的响应消息头,没有响应消息实体。
-
100 - Continue
客户端应该继续它的请求,这个暂时的响应用于通知客户端初始的请求已经被服务器接受,并且暂时没有被拒绝。此时客户端会继续发送剩余的请求,或者当所有请求已经发送完成时忽略该响应码。服务器必须在请求结束时发送一个最终的响应。
-
101 - Switching Protocols
服务器理解并打算执行客户端的请求,并且使用“Upgrade”字段头用户表示服务器会在这个连接中的协议升级到“Upgrade”头标识的版本号。
2xx: Success - The action was successfully received, understood, and accepted
这个系列的响应状态码表示客户端的请求已经成功的接收并处理。
-
200 - OK
请求成功处理。
-
201 - Created
请求成功处理并且新的资源被创建。新创建的资源可以使用URI标识,并且该URI在响应消息的Location头中。服务器在返回201响应时必须保证新的资源已经被创建,如果服务器在返回响应时还没来得及创建新的资源,服务器应该返回202(Accepted)响应。
201响应还可以包含“ETag”响应头,表示实体标签的当前值。
-
202 - Accepted
请求被接受并处理,但是处理还未完成。这个请求不一定被成功执行,并且也不会在有结果后重新异步发送响应消息。该响应状态主要用于一些类似batch的操作,当客户发送请求以后,不需要继续保持和服务器的连接。返回的消息实体需要包含请求当前的状态以及一个指向状态监视器或客户能得到结果的估计值。
-
203 - Non-Authoritative Information
响应消息实体头部返回的元信息不是在原始服务器有效的集合,而是从本地或第三方中拷贝收集。当前的集合可能是原始集合的子集或超集,这个响应码不是必须的,可以使用200(OK)替代。
-
204 - No Content
服务器已经成功的完成请求,该请求没有消息实体,只是返回一些最新的元信息。
-
205 - Reset Content
服务器已经成功的完成请求,客户端必须重置由该请求引起的文档视图。该响应主要用于清除用于之前输入的表单。该响应不可以包含消息实体。
-
206 - Partial Content
服务器已经成功完成“Partial GET”的请求。该响应的请求必须包含“Range”头,以及可选的“If-Range”头。响应必须包含以下头:Content-Range(或值为multipart/byteranges的Content-Type头)、Date、ETag或Content-Location、Expires、Cache-Control、Vary等。
3xx: Redirection - Further action must be taken in order to complete the request
这个系列的状态码表示为了完成当前请求,客户端必须要有进一步的处理。如果接下来的请求方法是GET或者HEAD,客户端可以自行发送接下来的请求,而不需要用于干预。并且客户端应该能检测到死循环以减少网络的堵塞。
-
300 - Multiple Choices
当前请求包含多个资源,并且在返回消息中包含每个资源的location信息。客户端可以根据一定的算法自行选择使用那个资源(没有定义算法)。服务器也可以指定一个推荐的选择(在Location头中),客户端可能会使用这个值重定向。
-
301 - Moved Permanently
请求的资源已经被永久的移动到一个新的URI上。客户端可以自动跳转到新的URI上。新的URI需要在响应消息的Location头中包含。
-
302 - Found
请求的资源临时的存在于另一个URI中。因为这个重定向还可能会改变,客户端需要继续使用旧的URI。临时的URI需要包含在响应消息的Location头中。
-
303 - See Other
请求的响应可以使用另一个URI中获得,并且必须使用GET方法获取另一个URI上的响应。该响应码主要用于将一个POST产生的输出重定向到一个新选择的资源上。新的URI需要在Location响应头中给出。
-
304 - Not Modified
如果客户端发送一个“Conditional GET”请求,并且该请求是被允许的,但是它所对应的文档没有改变,则服务器返回该响应。该响应不能包含消息体,但必须包含一些消息头:Date、ETag、Content-Location、Expires、Cache-Control、Vary。
-
305 - Use Proxy
请求的资源必须通过Proxy使用Location响应头中的URI访问。
-
306 - Unused
以前版本使用,现在已经不使用,但是响应码保留。
-
307 - Temporary Redirect
请求的资源临时的指向另一个URI,但是由于这个重定向可能会在将来被更改,因而客户端需要继续使用原来的URI。临时的URI在Location响应头中指定。
4xx: Client Error - The request contains bad syntax or connote be fulfilled
这个系列的响应码用于表示客户端错误请求,并且在响应实体消息中需要包含出错原因的解释(对HEAD的响应除外)。
-
400 - Bad Request
语法错误,请求不能被服务器理解。
-
401 - Unauthorized
请求需要包含用于认证。响应必须包含WWW-Authenticate头,包含请求认证需要的信息。客户端可以使用包含Authorization头重新发送请求。
-
402 - Payment Required
为将来使用保留。
-
403 - Forbidden
服务器拒绝该请求。如果服务器希望让客户端知道拒绝的原因,可以将原因放在响应消息体重,如果服务器想暴露该原因,则可以返回404(Not Found)响应。
-
404 - Not Found
服务器没有发现任何匹配的请求URI。如果服务器知道某些资源已经永久的被移出,并且没有重定向地址,则需要返回410(Gone)响应。该响应也可以用于服务器不想暴露客户请求被拒绝的原因。
-
405 - Method Not Allowed
请求方法不被对请求的资源允许。在响应消息中必须包含Allow头,指定请求资源允许的请求方法。
-
406 - Not Acceptable
请求的资源产生的响应包含了不被Accept请求头指定的特性。
-
407 - Proxy Authentication Required
类似401(Unauthorized),表示客户端必须在Proxy中通过认证。Proxy必须返回Proxy-Authenticate头,包含请求认证需要的信息。
-
408 - Request Time-out
服务器已经准备好并在等待,但是客户端在指定的时间里没有发送请求。
-
409 - Conflict
因为和资源当前状态冲突而导致请求没有完成。该响应码只有在用户知道任何解决这个冲突,并且重新提交请求时产生。
-
410 - Gone
请求的资源已经不在服务器上,并且没有更进一步的重定向地址。
-
411 - Length Required
请求消息必须包含Content-Length消息头。
-
412 - Precondition Failed
服务器对一个或多个请求消息头的测试失败。
-
413 - Request Entity Too Large
请求消息太大。如果这个条件是临时的,则服务器需要包含Retry-After响应头,表示这个响应时临时的,并在指定的时间以后重试。
-
414 - Request-URI Too Large
请求的URI太长。
-
415 - Unsupported Media Type
请求消息格式不被支持。
-
416 - Request range not satisfiable
在请求包含Range头,不包含If-Range头,并且请求的资源不在Range指定的范围中。响应头中需要包含Content-Range表示指定资源当前的长度。
-
417 - Expectation Failed
Expect请求头指定的值不能匹配服务器的逻辑。
5xx: Server Error - The server failed to fulfill an apparently valid request
这个系列的响应码用于表示服务器存在错误,不能完成相应的请求。服务器需要在响应消息体中包含出错的描述信息。
-
500 - Internal Server Error
服务器内部错误。
-
501 - Not Implemented
服务器没有实现当前请求。如没有实现对应的请求方法。
-
502 - Bad Gateway
代理或网关服务器从上游服务器中接收到一个不合法的响应。
-
503 - Service Unavailable
服务器因为临时负载过重或处于维护状态而不能处理请求。该响应暗示服务器当前的状态是临时的,如果服务器知道什么时候恢复可用状态,则可以包含Retry-After响应头,如果没有包含Retry-After头,则客户端可以把它视为500(Internal Server Error)来处理。
-
504 - Gateway Time-out
代理服务器或网关服务器在指定的时间内没有收到上游服务器的响应。
-
505 - HTTP Version not supported
服务器不支持或拒绝支持请求消息中指定的HTTP版本。
参考:RFC2616RFC1867http://blog.zhaojie.me/2011/03/html-form-file-uploading-programming.html
http://www.cnblogs.com/li0803/archive/2008/11/03/1324746.html
http://www.360doc.com/content/10/0930/17/3668821_57590979.shtml