So Easy系列之文件上传下载教程
所谓文件上传下载就是将本地文件上传到服务器端,从服务器端下载文件到本地的过程。例如目前网站需要上传头像、上传下载图片或网盘等功能都是利用文件上传下载功能实现的。
文件上传下载实际上是两步操作,第一是文件上传,就是将本地文件上传到服务器端,实现文件多用户之间的共享,第二是文件下载,就是将服务器端的文件下载到本地磁盘。
下面就文件上传与下载功能,分别学习。
首先,需要知道文件是如何实现上传及下载的。文件上传及下载实现原理如下:
文件上传实现原理分析
文件上传实现流程如下:
文件下载实现原理分析
文件下载实现流程如下:
在Web应用程序中实现文件上传功能,只需要在客户端页面中添加需要上传输入项,在服务器端Servlet中读取上传文件的数据,并保存在服务器端硬盘中即可。
客户端浏览器页面实现文件上传功能,具体代码如下:
需要注意的是:
完成客户端的文件上传功能之后,主要是在服务器端完成接收上传文件的数据内容。为了方便实现文件上传逻辑,可以使用第三方提供的文件上传包,具体如下:
commons-fileupload组件的官网地址:http://commons.apache.org/proper/commons-fileupload/。需要注意的是:在使用commons-fileupload组件时,需要依赖于commons-io包。commons-fileupload组件工作流程如下:
如何使用commons-fileupload组件实现文件上传功能,可以参考其官网的User Guide内容。
具体实现代码如下:
上述案例实现的是单文件上传,如果想实现多文件上传功能的话,服务器端的逻辑是一样的,也就是说,只需要在客户端页面实现多文件上传控件即可。动态实现多文件上传表单代码如下:
到目前步骤,已经可以成功从客户端浏览器向服务器端上传文件。但是上传的路径存在一些问题,上述上传路径是自定义的文件夹,而上传至这种自定义的文件夹后,通过浏览器可以正常访问,这是非常危险的。
例如一个用户上传一个JSP页面,然后通过浏览器访问该JSP页面,而该JSP页面中可以包含一些恶意代码。这时如果允许用户运行该JSP页面的话,可能会对服务器端造成很大影响。
所以,通常情况下,会将上传目录创建在Web工程的WEB-INF目录下。因为该目录下的内容,是无法通过浏览器访问到的。
获取文件上传路径的代码,应该修改为如下内容:
对于上传文件的名称,可能是文件的完整路径,例如:C:\upload\aaa.jpg。在服务器端只需要保存其上传文件的名称即可,所以需要对上传文件的名称进行进一步地处理,具体处理代码如下:
目前绝大多数的浏览器都不存在这个问题,仅仅只有一些比较老的浏览器版本存在,例如IE6.0版本。为保证兼容更多浏览器产品,这个问题依旧需要解决。
如果现在上传文件的名称为中文的话,会引起中文乱码问题。commons-fileupload组件为解决中文乱码问题提供了两种解决方案,如下:
一般情况下,不关心上传文件的内容,因为上传文件会保存在服务器端的磁盘中。但是,如果需要在控制台打印上传文件的内容,而刚好该上传文件的内容中包含中文的话,可以使用FileItem的getString(“UTF-8”)来处理编码。
如果同一个用户上传多个同名的文件,默认情况下会出现被覆盖的情况,即前一次上传的文件会被后一次上传的文件覆盖。而这种情况是不希望看到的,解决这个问题的方法就是可以为每一个上传的文件名称增加UUID,因为UUID类的randomUUID()可以生成一个唯一标识符。具体做法如下:
如果上传文件过多时,会导致上传目录中的文件过多,内容过大。这时可以考虑将不同文件存储在不同的目录中,而生成不同目录的规则参考如下:
这里以hashcode进行目录分离方式为例演示,具体思路如下:
其值为2dab369c-1e4f-4e58-8b61-13c7aef855b0。
其值为:166846237,转换成二进制后的值为:1001111100011101111100011101。
根据上述步骤,可以编写一个按照hashcode方式生成目录的工具类,具体代码如下:
上述程序代码可以改写如下:
上传文件时,用户可能上传非常大的文件,可能导致上传时占用过多资源。所以,对于用户上传的单个文件大小,应做出相应限制。利用ServletFileUpload的setFileSizeMax(long)方法进行设置,其中参数表示设置的大小,单位为字节数,例如servletFileUpload.setFileSizeMax(1024*10)表示上限为10KB。
一旦上传的单个文件大小超过限制大小时,会抛出FileUploadBase.FileSizeLimitExceededException异常,可以捕获该异常后向页面输出相应的错误信息。具体实现代码如下:
在实现多文件上传时,还需要设置上传文件的总大小。利用ServletFileUpload的setSizeMax(long)方法进行设置,其中参数表示设置的大小,单位为字节数,例如servletFileUpload. setSizeMax(1024*10)表示上限为10KB。
一旦上传的文件大小超过限制大小时,会抛出FileUploadBase.SizeLimitExceededException异常,可以捕获该异常后向页面输出相应的错误信息。具体实现代码如下:
一般情况下,上传文件默认都是先存储在内存中,然后在拷贝到服务器端的磁盘中。但是这样会有一些问题出现,例如单个文件过大时,占用服务器端资源会过多,导致服务器性能变差。这时可以通过手动设置文件缓存大小和上传文件的临时目录来解决。如果不设置上传的文件缓存大小,默认值为10KB,其表示如果上传文件小于10KB的话,会先存储在服务器端的内存中,如果上传文件大小大于10KB的话,会先存储在服务器端默认指定的临时目录中,而上传文件的默认临时目录为System.getProperty("java.io.tmpdir")。
手动修改上传文件缓存大小及临时目录的方式如下:
DiskFileItemFactory.setRepository(new File(getServletContext().getRealPath(临时目录相对路径)));
具体实现代码如下:
想要删除临时目录下的临时文件的话,只需要调用FileItem的delete()方法即可。
目前大部分具有文件上传功能的,在文件上传过程中,可以实时看到上传进度。可以使用ServletFileUpload提供的setProgressListener()方法实现,在客户端配置Ajax技术即可实现。由于目前没有掌握Ajax异步交互技术,所以只能在服务器端完成查看进度功能。
其中除使用setProgressListener()方法实现外,还需要计算如下几个结果:
根据上述内容,具体查看上传进度功能如下:
实现文件上传功能后,需要做的就是文件下载功能。文件下载不需要第三方组件支持,自定义完成即可,具体操作步骤如下:
下载文件功能实现后,还需要解决中文乱码问题。由于下载页面中的文件名是使用GET方式提交请求的,所以可以使用如下代码解决:
而上述代码只能解决服务器端接收客户端请求时参数的中文乱码问题,但是点击文件下载时的文件名称依旧是乱码的。这个问题是由于浏览器本身的问题,不同浏览器的解决方式不同:
可以通过Http请求协议的请求头中的“User-Agent”内容,判断客户端当前使用的浏览器是哪个产品。
通过网盘案例实现文件上传和下载功能,并且将文件上传相关信息保存到MySQL数据库表中。该案例实现原理分析如下:
MySQL数据库建表语句如下:
创建一个JSP页面用于网盘案例的主页面(提供文件上传功能和文件下载列表功能)。
首先实现网盘中的文件上传功能,具体实现步骤如下:
然后实现文件下载功能,具体实现步骤如下: