假如这个世界上只剩下你一个人,当你正坐在屋子里的时候,这时突然响起了敲门声...

Web上传文件的原理及实现

上一篇 / 下一篇  2008-05-29 19:03:28 / 个人分类:web

查看( 578 ) / 评论( 3 )
本文为原创,如需转载,请注明作者和出处,谢谢!ITPUB个人空间 I`XMBV7{-~5I

0@/B8Vk8zu6g A8M(I3h0    现在有很多Web程序都有上传功能,实现上传功能的组件或框架也很多,如基于java的Commons FileUpload、还有Struts1.x和Struts2中带的上传文件功能(实际上,Struts2在底层也使用了Commons FileUpload)。在asp.net中也有相应的上传文件的控件。
L \}T [0
Z1oZL#j*WGO Gu0    虽然现在有很多上传组件可以利用,但是了解Web上传文件的原理,对于处理突然出现的问题会有很大的帮助,下面就来讲一下通过浏览器上传文件的基本原理。在了解了原理之后,就可以非常容易地自制满足自身需要的上传组件了。
ZS$hl zm0众所周知,在客户端代码中需要使用<input type='file' name='file' />来选择要上传的文件,并上传,代码如上:ITPUB个人空间R7s4jk/fAT+r0WA

LO t9yT7]N$g0<html>ITPUB个人空间y(X|R_@ m(W
    <head>
\O-k xj q a\7~0        <title>upload</title>
7G&ml GV0        <meta. http-equiv="description" c>
|/t.vl?7g P~#r| N0        <meta. http-equiv="content-type" c>ITPUB个人空间6li5{1asb5S2g
    </head>ITPUB个人空间Y%N x4j2`N,sI%H*K

'} b-Vq-V)c%rR0    <body>
i u.F*o4}k+W;y0        <form. action="servlet/UploadFile" method="post"ITPUB个人空间.MjSJ2NV ~
            enctype="multipart/form-data">
i_,ORh'PH0            <input type="file" name="file1" id="file1" />ITPUB个人空间+s}?%y2Fub7v,ki
            <input type="file" name="file2" id="file2" />
B o\ _ i0            <input type="submit" value="上传" />ITPUB个人空间C"@+NV5e+EqR
         </form>ITPUB个人空间)O6d+E5QH lk4{
    </body>
%?%x^"qo7eM.i0</html>
/V],m ? Q3s XLU'E P-zm0
|E8w'R/^$H0    从上面的代码可以看出,有两个文件选择框(file1和file2),在上传文件时,<form>标签必须加上enctype="multipart/form-data",否则浏览器无法将文件内容上传到服务端。下面我们来做个实验。在Servlet的doPost方法中编写如下的代码,如果想使用asp.net或其他的语言或技术,也可以很容易实现相应的功能。
$Y(M| HW7j!C~4ONJ;u0    public void doPost(HttpServletRequest request, HttpServletResponse response)ITPUB个人空间j R0Dg^Mg)~2@
            throws ServletException, IOException
R4},\-`xV]#z-X0    {
"s Wm y }'V\h0        java.io.InputStream is = request.getInputStream();
p!~ M V pxg0        java.io.FileOutputStream fos = new java.io.FileOutputStream("d:\out.txt");ITPUB个人空间 ss-goP2X
        
^aVH&[l!v/Q0        byte[] buffer = new byte[8192];ITPUB个人空间"y}g!['^
        int count = 0;
|0r/~l3iF0        while((count = is.read(buffer)) >0)ITPUB个人空间aZSKal
        {ITPUB个人空间.P `:c jt
            fos.write(buffer, 0, count);
(N6f2I.P/[_Qh0        }        ITPUB个人空间$c*^Mi2Q#RX]?b
        fos.close();
_y Ke/RP0    }
6~/O;o0Yl*K0ITPUB个人空间XM#t*ho
    上面的功能非常简单,只是通过request获得一个InputStream对象,并通过这个对象从客户端获得发送过来的字节流(注意,一定要用字节流,因为,上传的文件可能是二进制文件,如图象文件,因此,使用字节流会更通用)。并将这些字节流保存在D盘的out.txt文件中。然后我们打开out.txt,文件的内容如图1所示:
O;uyN1Pt(w5`A*M0ITPUB个人空间 g*K#G"_HF(?z(U{

mU\!D4[ ^J _:_`0
p6^$}5wv+\9Yu:b~001_webupload1.jpg

cBEil.m Id0ITPUB个人空间LO@V3a8bI?
图1ITPUB个人空间-p2Z Xw3dV;e I

$o5d%Eru]/hL B UC5C0    由于out.txt是使用文本形式打开的,并且file1上传的是a.jpg(一个图象文件),因此,显示的是一些乱码。我们可以不用管它们。只需要看看这些内容的头部。我们很快就可以找到规律。每一个文件内容的头部都由“-----------------------------30514443229777”分隔,然后是这个文件的属性,如下:ITPUB个人空间%E2S g f.HTkb
ITPUB个人空间#}(?ioJ si6k,`$I!L
Content-Disposition: form-data; name="file1"; filename="a.jpg"ITPUB个人空间 j7xD)m i)@Hc
ITPUB个人空间_+g:W*n7q:|2u
Content-Type: image/jpeg
xsfh F'B0
+t3VniA6b-s0    其中包含了文件选择框的name属性,还有上传的文件名(filename字段),要注意的,firefox在上传时,这个filename属性值只是文件名,如果使用IE,就是带路径的文件名,如D:"a.jpg。ITPUB个人空间:X mwBZ}_1m$?

UP%t^'``!K$ehHcoO{0    接下来的规则就和HTTP的头一样了,以一个空行("r"n)分隔。后面就是文件的具体内容。现在最关键的文件的结尾,从图1可以看出,文件的结尾也是“-----------------------------30514443229777”,因此,可以断定,第一个上传的文件(包括文件头)是夹在两个“-----------------------------30514443229777”之间的。而“-----------------------------30514443229777”就是multipart/form-data协议的分隔符。但这里还有一个最关键的问题。这个分隔符每次上传都不一样,服务端是如何知道每次上传的这个分隔符的呢?ITPUB个人空间f#b#jP/B)Eu J(S

@ R&|+L1fN3w0实际上,这个分隔符是通过HTTP请求头的Content-Type字段获得,可通过下面的代码输出这个字段值:ITPUB个人空间 VD6_/?caZh*s
ITPUB个人空间;HyP6m.Mrc
System.out.println(request.getHeader("Content-type"));
OnZ zpD%Q V }C0ITPUB个人空间&L/v?3d5Y'_!K9?x$H
输出的内容如下:
X}E)wG,i7M0ITPUB个人空间0nXz-Pw,r5t5Ur
multipart/form-data; boundary=---------------------------106712230227687ITPUB个人空间k)z"A4V(Xn
ITPUB个人空间ei ^ [.['[E-F
    只要在服务端获得boundary后面的值即可。经过测试,Content-Type中的分隔符号中的“-”比实际上传的“-”少两个,不知是怎么回事。不过这没关系,我们可以认为每一个文件块是以""r"n—“结尾的,或是直接将从boundary获得的分隔符加两个“—”。而最后结尾的分隔符是“---------------------------106712230227687—”,后面多了两个“—”。
,cjR4_*e0
I#v.Y&zE L.T~0    综合上述,也就是说,一个文件块是以“---------------------------106712230227687”开头,以“—”结尾,从图2可以看出这一切。
F+T2YQ*l0ITPUB个人空间M3a$rLg Y xS%_ d
ITPUB个人空间7k_U:a S5@
ITPUB个人空间3taK[g)cb
02_webupload.jpg

? T[rR$Pw0ITPUB个人空间'r$AHK"~!k
图2
(x;\ kt3x-B0
3[ M%t-dS"zV0    至于剩下的工作,就是按着上面的规则来分析这些字符流了。分析的方法很多。在这里就不详述了。ITPUB个人空间 H-OE4W+UEAu1zZ
ITPUB个人空间Z my)|/} |D6`
multipart/form-data规范原文:http://www.ietf.org/rfc/rfc2388.txt

TAG: java web 上传文件

justforregister的个人空间 justforregister 发布于2008-05-31 23:57:50
不错, 好文
Sky-Tiger的个人空间 Sky-Tiger 发布于2008-06-01 21:12:38
GOOD!
胡言乱语 john3000 发布于2008-06-02 15:47:27
thanks
我来说两句

(可选)

日历

« 2008-09-06  
 123456
78910111213
14151617181920
21222324252627
282930    

数据统计

  • 访问量: 9799
  • 日志数: 106
  • 建立时间: 2008-02-08
  • 更新时间: 2008-09-05

RSS订阅

Open Toolbar