#文件上传 文件的上传其实是比较简单的,只要实现两步就可以了:
- 在web页面中添加上传输入项
- 在servlet中读取上传文件的数据,并保存到本地磁盘中
###添加上传输入项也需要两步
- 使用
<input type="file">
标签,一定要设置name属性,不然不会被提交 - 将所在form的 enctype 属性设置为 multipart/form-data,设置该值后,浏览器就会把上传的文件数据附带在http请求的消息体中,并使用MIME协议对上传文件进行描述
###在Servlet中获取数据
如果提交的表单使用了 multipart/form-data,那么就Servlet中就不能直接用requets对象来获取数据了,因为如果使用了 multipart/form-data 设置表单,浏览器就会使用MIME协议来描述表单的所有项。那么如果要获取数据,就需要使用request对象来获取输入流,然后对流进行MIME解析。对MIME解析不是一件容易的事情,因为上传的文件类型会非常多,而个数不定。所以一般在实际开发中,一般使用第三方上传组件来完成文件上传功能。
第三方上传组件比较多,比较常用的有:Commons-fileupload、Cos组件等
###Commons-fileupload Commons-fileupload是Apache基金会的一个开源组件,该组件性能优异,并且API使用也比较简单。如果要使用该组件,需要给应用添加两个Jar包:
- Commons-fileupload
- Commons-io(Commons-fileupload组件从1.1版本开始,依赖该Jar)
###Commons-fileupload组件工作图:
根据上图的步骤,可以看出,步骤非常简单:使用DiskFileItemFactory获取一个ServletFileupload对象,然后获取FileItem即可。获取到FileItem之后,也有对应的API来获取FileItem的一些信息:
- 普通字段:
isFormField() getFieldName() getString()
- 上传文件:
getInputStream() getName()
demo示例多个文件上传的处理。(对于ie6的绝对路径的截取来获取文件名)
#上传需要处理的细节 上传其实并不难,主要是需要细心,去处理很多细节,来把程序写得易用。
###乱码问题
- 上传文件的乱码问题,直接使用ServletFileupload对象的
setHeaderEncoding()
方法来解决 - 普通输入项的乱码解决,只能使用手动来转换,使用request设置编码已经没有用了,
inputValue = new String(inputValue.getBytes("ISO8859-1"), "UTF-8")
,也可以直接使用FileItem的getString方法,这个方法被重载了,也设置编码,其实内部实现代码就是手动转码的代码。
###判断表单类型
- 在开发中,应该首先判断提交的表单是否 multipart/form-data 类型的表单。使用 ServletFileupload 对象的
isMultipartContent()
方法来进行判断 - 如果是普通表单,就应该使用普通方法来进行处理,如果是上传表单,就使用组件处理
###设置缓冲区 在上传中,很有可能有比较大型的文件,那么对于大型文件的上传处理,比较好的处理就是使用缓冲区来进行处理。fileupload组件支持内存缓冲区和临时文件缓冲区。 当文件不是很大的时候,直接使用内存缓冲区来处理,但是如果文件太大,内存缓冲区就不过用了,就要使用临时文件来处理,这个具体的临界值要根据实际的服务器环境来恒定,使用了临时文件后,对应的输入流就是从临时文件来读取数据了。
-
使用 DiskFileItemFactory 对象来设置缓冲区: * `setSizeThreshold(int sizeThreshold)`,设置内存缓冲区的大小,默认值为10K。当上传文件大于缓冲区大小时,将使用临时文件缓存上传文件 * `setRepository(java.io.File repository)`,指定临时文件目录,默认值为 System.getProperty("java.io.tmpdir") * 可以在工厂创建的时候指定这些值
有一点要特别注意,如果使用了临时文件存储,临时文件是不会自动删除的,在上传结束后,需要手动删除fileitem.delete()
###上传的安全隐患 一般的,不要将上传文件的保存目录设置在项目根目录中,这是一件很危险的事情。因为别人可以将一个写好的jsp上传上去,然后通过客户端让其运行,如果该jsp中有恶意代码,将会攻陷服务器(shutdown -s -t 200
,shutdown -a
)。 应该将上传文件的目录保存到WEB-INF文件夹中,这个文件夹是禁止非本应用资源访问的。
###上传文件的类型 这个其实很简单,首先定义一个可上传类型的集合或者枚举,然后对上传文件的名字进行验证就可以了。
###上传文件的大小 上传是由解析器来处理的,所以设置上传文件大小的设置在ServletFileupload中,它的常用方法有:
bololean isMultipartContent(HttpServletRequest request)
,判断上传表单是否为 multipart/form-date 类型List parseRequest(HttpServletRequest request)
,解析request对象,封装成FileItem集合setFileSizeMax(long fileSizeMax)
,设置文件上传最大值setSizeMax(long sizeMax)
,设置上传文件总量的最大值setHeaderEncoding(java.lang.String encoding)
,设置编码格式
###多文件上传的空文件 在实际开发中,一般都是对一个文件的处理,多文件并不多,多文件的上传本身就相对要考虑的问题多一些。如果是多文件,而用户没有全部选择文件,这时也是很容易导致后台程序出错的,所以在后台也要做这方面的处理。(非空验证,如果是空,就不要处理等)
###上传文件的名称 在做上传的时候,很可能出现重复的文件名称,那么就需要给每个上传的文件一个唯一的名称。常见的命名策略有:使用UUID+文件名称、使用毫秒数+随机数+文件名称等等。 在这个过程中有一个问题,那就是把所有的文件是否存在一个文件夹中呢?答案肯定是不能。对于操作系统来说,一个文件夹的文件过多,那么在查找的时候必然会影响性能,所以对于上传的文件,不光要考虑文件名称的唯一性,还需要考虑怎样提升查找的性能。
一般的做法是按照年月日去创建文件夹目录结构,这样就可以很容易的解决掉这个问题。 还有一些使用算法来实现的,这种情况一般应用在特别多的上传量的情况下(base64计算文件夹,hash值计算文件夹)。