如今有不少Web程序都有上传功能,实现上传功能的组件或框架也不少,如基于java的CommonsFileUpload、还有Struts1.x和Struts2中带的上传文件功能(实际上,Struts2在底层也使用了CommonsFileUpload)。在asp.net中也有相应的上传文件的控件。
虽然如今有不少上传组件能够利用,可是了解Web上传文件的原理,对于处理忽然出现的问题会有很大的帮助,下面就来说一下经过浏览器上传文件的基本原理。在了解了原理以后,就能够很是容易地自制知足自身须要的上传组件了。
众所周知,在客户端代码中须要使用<input type='file' name='file' />来选择要上传的文件,并上传,代码如上:html
<html>
<head>
<title>upload</title>
<meta http-equiv="description" content="this is my page">
<meta http-equiv="content-type" content="text/html; charset=GB18030">
</head>
<body>
<form action="servlet/UploadFile" method="post" enctype="multipart/form-data">
<input type="file" name="file1" id="file1" />
<input type="file" name="file2" id="file2" />
<input type="submit" value="上传" />
</form>
</body>
</html>
从上面的代码能够看出,有两个文件选择框(file1和file2),在上传文件时,<form>标签必须加上enctype="multipart/form-data",不然浏览器没法将文件内容上传到服务端。下面咱们来作个实验。在Servlet的doPost方法中编写以下的代码,若是想使用asp.net或其余的语言或技术,也能够很容易实现相应的功能。java
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { java.io.InputStream is = request.getInputStream(); java.io.FileOutputStream fos = new java.io.FileOutputStream("d:\\out.txt"); byte[] buffer = new byte[8192]; int count = 0; while((count = is.read(buffer)) >0) { fos.write(buffer, 0, count); } fos.close(); }
上面的功能很是简单,只是经过request得到一个InputStream对象,并经过这个对象从客户端得到发送过来的字节流(注意,必定要用字节流,由于,上传的文件多是二进制文件,如图象文件,所以,使用字节流会更通用)。并将这些字节流保存在D盘的out.txt文件中。而后咱们打开out.txt,文件的内容如图1所示:浏览器
因为out.txt是使用文本形式打开的,而且file1上传的是a.jpg(一个图象文件),所以,显示的是一些乱码。咱们能够不用管它们。只须要看看这些内容的头部。咱们很快就能够找到规律。每个文件内容的头部都由“-----------------------------30514443229777”分隔,而后是这个文件的属性,以下:
Content-Disposition: form-data; name="file1"; filename="a.jpg"
Content-Type: image/jpeg
其中包含了文件选择框的name属性,还有上传的文件名(filename字段),要注意的,firefox在上传时,这个filename属性值只是文件名,若是使用IE,就是带路径的文件名,如D:"a.jpg。
接下来的规则就和HTTP的头同样了,以一个空行("r"n)分隔。后面就是文件的具体内容。如今最关键的文件的结尾,从图1能够看出,文件的结尾也是框架
“-----------------------------30514443229777”,所以,能够判定,第一个上传的文件(包括文件头)是夹在两个asp.net
“-----------------------------30514443229777”之间的。而“-----------------------------30514443229777”就是multipart/form-data协议的分隔符。但这里还有一个最关键的问题:这个分隔符每次上传都不同,服务端是如何知道每次上传的这个分隔符的呢?post
实际上,这个分隔符是经过HTTP请求头的Content-Type字段得到,可经过下面的代码输出这个字段值:
System.out.println(request.getHeader("Content-type"));测试
输出的内容以下:
multipart/form-data; boundary=---------------------------106712230227687
只要在服务端得到boundary后面的值便可。通过测试,Content-Type中的分隔符号中的“-”比实际上传的“-”少两个,不知是怎么回事。不过这不要紧,咱们能够认为每个文件块是以""r"n—“结尾的,或是直接将从boundary得到的分隔符加两个“—”。而最后结尾的分隔符是“---------------------------106712230227687—”,后面多了两个“—”。ui
综合上述,也就是说,一个文件块是以“---------------------------106712230227687”开头,以“—”结尾,从图2能够看出这一切。this
图2
至于剩下的工做,就是按着上面的规则来分析这些字符流了。分析的方法不少。在这里就不详述了。spa