关于文件下载

文件下载,可以通过Servlet中的写法,也可以通过Struts框架做。已经做过这个功能很多次了。用的多了,这次突然发现了一个问题。

在项目中,做了一个下载功能,在好几个页面都用了这个功能,相同的方法,在不同页面引用。

调用这个功能,用URL重写的方式调用action中的方法。 

这里的下载功能没有使用struts框架,用的servlet中的

 一、探索过程

(1)刚开始我以为,在不同页面上调用这个方法,方法虽然相同,但调用方法后回到的页面不同。所以我把这个公用的方法提出来,作为action中private方法,不同页面请求Action中的不同方法,这些方法都调用这个private方法,这些action返回值回到各自的页面。但后来证明不需要这么麻烦。就把这个称为方法一。

(2)然后我觉得方法一太麻烦了。想把下载方法放到action中,然后在页面上用ajax访问这个action中的方法。不同的页面都走同样的ajax请求,这样,这个方法就复用了。因为项目框架中用的是dwr。所以我也直接用dwr发送这个请求了。写完之后,下载功能报错。调试后发现,是在HttpServletResponse response=ActionContext.getHttpServletResponse();时报的错response获取不到,后来我换成ResponseAware注入到Action中的方式获取response,还是获取不到,response为null。上网查原因,网上是这么说的:

  Action中获取response有两种方式:ActionContext.getHttpServletResponse()和ResponseAware注入。只有当请求通过struts.xml文件时,才能获取到response。

因为dwr并没有走struts.xml,而是通过自己的配置文件dwr.xml,所以通过dwr向action发送请求,是获取不到response的。没有response也就无法使用FileOutputStream out=response.getOutputStream();没有输出流,文件下载就失败。就把这个失败的方法称为方法二吧。

(3)既然dwr不行,然后我就想换成jQuery来做这个ajax效果。jQuery中ajax请求是走struts.xml的,获取response肯定没问题。把这个还未用实践尝试的方法称为方法三。

(4)中间和同事说了,他也是突然一愣。我俩讨论了下,发现:

action中下载的方法,代码大致如下:

 1 public String downloadFile(){
 2     //要下载的文件
 3     File file = new File("F:/upload/课程.png");
 4     //说明下载文件的类型
 5     resp.setContentType("image/png");//应该写一个工具类或方法,根据文件名后缀,获得文件MIME类型
 6     //说明下载文件的长度,进度条,以便用户看下载进度
 7         resp.setContentLength(Long.valueOf(file.length()).intValue());
 8     //文件名有中文时,为了在弹出的保存框中显示中文,转码。浏览器会相反方向转码,即ISO8859-1→UTF-8
 9     String fileName=new String("1.png".getBytes("UTF-8"),"ISO8859-1");
10     //出现弹出框,并显示下载的文件名
11     resp.setHeader("Content-Disposition", "attachment;filename=""+fileName+""");//响应头
12         
13     InputStream inputStream = new FileInputStream(file);
14     //获得响应输出流,输出到网页上
15     OutputStream outputStream =resp.getOutputStream();//返回ServletOutputStream
16         
17     BufferedOutputStream bos=new BufferedOutputStream(outputStream);
18     BufferedInputStream bis=new BufferedInputStream(inputStream);
19         
20     byte[] buffer=new byte[100];//缓冲区大小影响下载速度
21     int len=-1;
22         
23     while((len=bis.read(buffer))!=-1){
24         bos.write(buffer, 0, len);
25     }
26     bos.flush();
27     bos.close();
28     bis.close();
29         
30     System.out.println("下载完成!");
31     
32     return null;  
33 }
View Code

执行下载程序的时候,方法可以返回null或者没有返回值。下载程序返回null或者没有返回值的时候,不用给struts.xml中的action配置<result>,下载之后,页面也不会跳转,仍然保持在原页面。所以,action中的这个方法是可以在系统中的不同页面上直接调用的,这个方法也不用再像上面那样各种处理,直接调用就行了。如:

1 <input type="button" onclick="download('${ctx}',${id});" value="下载" />
1 function download(ctx,id){
2     location.href=ctx+"/doActivity/downloadFile.do?id="+id;
3 }
1 <action name="downloadFile" class="activityOrganAction" method="downloadFile">
2 </action>

除了Ajax的请求,这个方法和方法三几乎没有什么不同。

二、总结

当:多个页面共用一个下载方法,方法后台没有返回值或者返回null,那么在struts.xml中不用配置<result>,下载之后页面会停留在当前页面,不会跳转。在这些页面可以直接调用这同一个下载方法。

代码写久了,下载的方法用多了,慢慢也就在这些细节上模糊了,这次算是弄清楚这个问题了。

2018.4.2

action属于MVC的C层,公用的部分不适合在这里做公共的。

下载的方法属于共用方法,这里用的是servlet,可以抽出来放到工具类中,如PubMethod,或PubUtil,由各个action调用,把file传参。

原文地址:https://www.cnblogs.com/mySummer/p/4614776.html