学习任何一个MVC框架无非需要学习以下内容:
1、环境的搭建
2、参数的传递(前台与后台双向)
3、数据验证
4、文件的上传下载
5、异常处理
6、国际化
今天我们看一下在web开发中离不开的文件上传与下载。
一、上传的原理
任何web应用上传文件都是从HttpservletRequest中解析表单域(普通域与文件域),然后以二进制流的方式解析内容,所谓的上传组件(common-fileupload或者COS)都是帮助开发者解析request对象,然后提供更方便的方法去得到吸相关的信息。
对于一般的web应用,大部分都是通过表单Form进行文件上传,而利用表单进行上传需要设置form的 enctype=matipart/form-data属性(当然如果不设置enctype=matipart/form-data则完不成文件上传),该属性会使浏览器把文件以二进制流的方式提交请求而不是字符串,当以二进制流方式提交后在服务器端是无法通过request的getParameter拿到参数,但可以从二进制流中拿到文件内容,从而完成文件上传。
二、各种上传演示
1、手动上传
(手动控制二进制流,一般会用到java的两个类:URLEcoder、URLDecoder)
一般文件(这里以txt文件为例),上传提交请求后请求中会是如下格式:
-----------------------------------这里是一串数字(其实是提交按钮)
Content-Disposition : form-data ; name=''file''; filename=''a.txt''
Content-Type : txt/plain
换行
换行
这里是文件内容
--------表单域
-------表单域
==========================================================
通过对enctype=matipart/form-data的表单的提交后的请求的打印,可以发现,在请求中每个表单域的开始都是以“------------------”开始,对每个文件域而言,其第二行总是会出现“filename=a.txt ”的字符串。
下面以在jsp中完成上传:
//取得HttpservletRequest的inputstream流 InputStream is = HttpservletRequest.getInputStream(); //包装 BufferedReader br = new BufferedReader(new InputStreamReader(is)); String buffer = null ; //循环读取请求内容的每一行 while((buffer = br.readLine()) != null){ //如果读到的内容以"----------------------"开始 //以"--"结束表明已到请求结尾。 if(buffer.endWith("--") && buffer.startWith("--------------")) { break ; } //如果读到的内容以"----------------------"开始, //则表明开始了一个表单域 if(buffer.startWith("------------------")){ //跳过两行,则开始处理上传文件内容 br.readLine(); br.readLine(); //以系统时间构造文件名 File file = new File(request.getRealPath("/")+ System.currentTimeMillins()); //创建文件输出流 PrintStream ps = new PrintStream( new FileOutPutStream(file)); String content = "" ; //读取文件内容 while((content = br.readLine())!= null){ if(content.endWith("--") && buffer.startWith("--------------")) { break ; } ps.printLine(content) ; } ps.flush(); ps.close(); } }
注意:
上面用的是BufferedReader,是针对字符串的,当然如果将字符流改为字节流则可以处理任何文件上传。
2、使用框架(commons-fileupload或者COS)
注意:此处只用上传框架而不是struts框架,会用到上传框架自身的API去对每一种表单域进行分析获取其内容。
3、使用struts2框架
在struts2中有一个配置文件:struts.properties,其用于配置struts2文件上传文件的解析器。
struts2默认使用 apache的common-fileupload上传文件。
#指定用COS上传
struts.mutipart.parser=cos
==================
要使用不同的上传组件可以直接利用 struts.mutipart.parser在配置文件中指定。
总而言之,利用struts2上传文件只做:
1)、配置上传解析器
2)、加入相关的上传组件的jar包
3)、建立action
(此处的action有属性:
String title、File upload、String uploadContentType、String uploadFileName
String savePath(该属性是在struts.xml中配置的属性)
当然这些属性需要get set 方法
)
4)配置action
与普通的action配置没多大区别只是增加一个参数:
<param name="savePath">/upload</param>
5)文件过滤
a、手动过滤
利用action中的方法得到文件类型、大小、长度等从而进行限制。
b、拦截器实现过滤
struts2提供了文件上传的拦截器:fileUpload,只需将其加载action配置中即可:
<action ......> <inteceptor-ref name="fileUpload"> <param name="allowedTypes"> image/bmp ,image/gif </param> <param name="maximumSize">2000000</param> </interceptor-ref> </action>
三、struts文件下载
struts2提供了stream结果类型,该结果类型是专门用于支持文件下载。
指定stream结果类型时,需要指定一个inputName参数,该参数指定了一个输入流,这个输入流是被下载文件的入口。
struts2的下载action与普通action没什么不同,仅仅是该action需要提供一个返回InputStream流的方法,该输入流代表了该下载文件的入口。
action代码如下: public class FileDownloadAction extends ActionSupport { //该属性是依赖注入的属性,可以在配置文件中动态指定 private String inputPath ; //上述属性的set方法 //定义一个返回InputStream的方法,该方法将作为下载文件的入口方法 //且需要配置stream类型结果时指定inputName的参数,inputName参数的 //值就是方法去掉get前缀、首字母小写的字符串 public InputStream getTargetFile() { //ServletContext提供getResourceAsStream方法,返回 //指定文件对应的输入流 return ServletActionContext.getServletContext() .getResourceAsStream(inputPath); } } 注意: 该方法中有一个getTargetFile()方法,该方法返回一个inputstream,该方法的名字getTargetFile,则stream类型的结果映射中inputName参数的值为targetFile。 ========================= action配置: <action name="fileDownload" class ="..."> <!--指定被下载资源的位置--> <param name="inputPath">\images\aa.jpg</param> <result name="success" type="stream"> <!--指定被下载文件的类型--> <param name="contentType">image/jpg</param> <!--指定有getTargetFile方法返回被下载文件的InputStream--> <param name="inputName">tartgetFile</param> <param name="contentDisposition">filename="wjd.jpg" </param> <!--指定下载文件的缓冲区的大小--> <param name="bufferSize">496</param> </action> ============= 界面连接: <a href="fileDownload.action">下载文件</a>