Struts2 Annotation实现文件下载功能
一、达到目标:
给定任意Web根路径下面的文件相对路径下载文件,在任意浏览器下下载文件不出现乱码问题;
二、开发要点:
- Annotation: 经历了Struts1的大量Action配置之后到了Struts2坚决放弃了xml配置,虽然struts2的xml配置比较简单了,但是我真的很懒…… 所以这里实现全部是有注解实现,简单、方便、容易日后的重构
- 文件名乱码 提高这个问题都想吐,为什么不全部使用UTF-8编码呢…… 既然不能改变那就只能解决,所以这里要考虑各个浏览器对文件名的编码解析
三、具体实现
不说废话,直接上代码,下面再讲解;如果想下载猛击这里
package cn.wsria.demo.web.file; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import org.apache.commons.lang.StringUtils; import org.apache.struts2.ServletActionContext; import org.apache.struts2.convention.annotation.Namespace; import org.apache.struts2.convention.annotation.Result; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Controller; import org.springside.modules.web.struts2.Struts2Utils; import cn.wsria.util.BrowserUtils; /** * 下载Action * * @author HenryYan * */ @Controller @Namespace("/file") @Result(type = "stream", params = { "contentType", "application/octet-stream;charset=ISO8859-1", "inputName", "inputStream", "contentDisposition", "attachment;filename=${downFileName}", "bufferSize", "4096" }) public class DownloadAction { private static Logger logger = LoggerFactory.getLogger(DownloadAction.class); private String fileName; private String downFileName; public void setFileName(String fileName) throws UnsupportedEncodingException { logger.debug("得到原始文件名:{}", fileName); // 转码 fileName = URLDecoder.decode(fileName, "UTF-8"); logger.debug("转码后的文件名:{}", fileName); this.fileName = fileName; } /** * 如果传过来的fileName参数有路径处理后只返回文件名 * @return 例如path/aaa.txt,返回aaa.txt * @throws UnsupportedEncodingException */ public String getDownFileName() throws UnsupportedEncodingException { fileName = StringUtils.defaultIfEmpty(fileName, ""); // 判断path/aaa/bbb.txt和path\aaa\bbb.txt两种情况 String tempFileName = StringUtils.substringAfterLast(fileName, "/"); if (tempFileName.length() == 0) { tempFileName = StringUtils.substringAfterLast(fileName, "\\"); } if (tempFileName.length() == 0) { tempFileName = fileName; } // 处理中文 logger.debug("去除路径信息后得到文件名:{}", tempFileName); // 处理IE浏览器乱码问题 if (BrowserUtils.isIE(Struts2Utils.getRequest())) { downFileName = java.net.URLEncoder.encode(tempFileName, "UTF-8"); } else { downFileName = new String(tempFileName.getBytes(), "ISO8859-1"); } return downFileName; } public InputStream getInputStream() throws UnsupportedEncodingException { return ServletActionContext.getServletContext().getResourceAsStream("/" + fileName); } public String execute() { return "success"; } }
Struts2本身就支持文件下载的输出类型,所以有一个type叫做“stream”,输出文件流到客户端,也就是Result中配置的type = “stream”
- contentType设置为application/octet-stream;charset=ISO8859-1,作用是设置输出的类型为二进制流
- inputName设置为inputStream指定Action中的哪一个方法返回二进制输出流
- contentDisposition设置为attachment;filename=${downFileName},attachment标示当客户端打开下载链接的时候弹出保存对话框;filename=${downFileName}就是要指定下载的文件名名称,${downFileName}是指从Action中读取getDownFileName方法,这里一定要提供getDownFileName方法,我做的时候脑子短路调试了好一会,原来在生成getter和setter的时候没有生成getDownFileName方法
好,现在如果在项目的webapp/file中放置一个aaa.txt,那就可以使用路径:http://localhost:9000/wsria-demo/file/download.action?fileName=files/aaa.txt访问了 但是如果要下载中文的呢?
这里在开发的时候又被乱码问题玩了一把,最终的解决办法也是最可靠的办法就是前后台编码和解码,具体实现如下: 在前台下载的时候用encodeURI方法编码下载路径,把中文字符转换为UTF-8编码,为了统一不出问题所以我写了一个方法:
function download(fileName){ var downUrl = $.common.custom.getCtx() + '/file/download.action?fileName=' + fileName; open(encodeURI(encodeURI(downUrl))); }
函数所在文件:common.js
四、解决文件名乱码
1、接着在后台进行解码工作,在setFileName的时候解码文件名,fileName属性是在读取本地文件时使用的,所以要把中文的UTF-8还原回来交给java的输入流
2、在输出文件的时候客户端根据浏览器不同解析文件名的方式也不同,如代码的第67~71行处就是根据浏览器不同设定不同的文件名
就讲到这里了,试试吧,有问题在此文后面留言或者在关于中联系我!