使用浏览器向服务器上传文件其本质是打开了一个长连接并通过TCP方式传输数据。而需要的动作是客户端在表单中使用file域,并指定该file域的name值,然后在form中设定enctype的值为multipart/form-data和method值为post。其他就如同一般表单一样。示例如下:
<form name="form" action="uploadbyservlet1" enctype="multipart/form-data" method="post">
上传文件<input type="file" name="file"/><br>
<button type="submit">上传</button>
</form>
而服务端接受到的表单数据示例如下:
------WebKitFormBoundaryHn0SIY7N2MBTtS5n
Content-Disposition: form-data; name="file"; filename="自我之歌.txt"
Content-Type: text/plain
我的世界只有那么一天
起于今天的晨曦
终于明天的黎明
烦恼的终将被阳光照淡
欢喜的也将岁黑夜散去
体验的同时愿将过去遗忘
遗忘的同时也将寄予明天
------WebKitFormBoundaryHn0SIY7N2MBTtS5n--
其中“------WebKitFormBoundaryHn0SIY7N2MBTtS5n-- ”是类似于分隔符这样的作用,在其中描述了相关的所有信息,如文件名、file域名称、文件类型和文件内容。而当有多个file域上传文件时,表单内容是用分隔符将不同file域的内容分割开。因此使用servlet上传文件要解决的问题就是如何从表单数据中提取对应文件的文件名、类型和内容。因此便有如下解决方法:
1. 如果是文本文件,因为内容的标识是明显的,所以可以按行读取的方式找到文件名和文件内容,然后写入文件;
2. 如果是二进制文件,则可以使用1中的方式找到文件内容的位置,然后使用BufferedInputStream这样的可以进行skip跳读的流来封装输入流,找到内容数据位置后,使用skip来调到该位置,从而读取内容数据
而上面这些都是Sevler3之前的操作方式,可以看出来需要处理很多重复的步骤。而使用Servlet3就可以节省很多功夫。Servlet3中将上传文件封装在一个Part对象中,一个文件域对应一个Part,可以通过request.getParts()来获得所有的Part,在Part中通过getHeader("content-disposition") 可以获得上传文件的信息,从中提取出文件名。然后使用Part.write方法将文件保存在本地。但这只能在Servlet3下使用,而且必须给Servlet添加@MultipartConfig才可以使用Part来保存文件。