文件下载的核心思想即是将文件从一个地方拷贝到另一个地方.
1.传统方式:
在Action中加入大量servlet api 操作.优点是好理解,缺点是耦合度高。
2.stream方式:
使用struts2中的stream拦截器进行操作
二.实例:
我这里用的是maven,贴出pom.xml:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.amos</groupId> <artifactId>struts2_learn</artifactId> <packaging>war</packaging> <version>0.0.1-SNAPSHOT</version> <name>struts2_learn Maven Webapp</name> <url>http://maven.apache.org</url> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <dependency> <groupId>org.apache.struts</groupId> <artifactId>struts2-core</artifactId> <version>2.3.16</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-io</artifactId> <version>1.3.2</version> </dependency> <dependency> <groupId>jstl</groupId> <artifactId>jstl</artifactId> <version>1.1.2</version> <scope>provided</scope> </dependency> <dependency> <groupId>taglibs</groupId> <artifactId>standard</artifactId> <version>1.1.2</version> </dependency> </dependencies> <build> <finalName>struts2_learn</finalName> </build> </project>
1.使用传统方式实现文件下载:
DownloadAction.java
package download; import java.io.FileInputStream; import java.io.InputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletResponse; import org.apache.struts2.ServletActionContext; import com.opensymphony.xwork2.ActionSupport; /** * @ClassName: DownloadAction * @Description: 文件下载 * @author: amosli * @email:amosli@infomorrow.com * @date Feb 13, 2014 1:22:23 AM */ public class DownloadAction extends ActionSupport { private static final long serialVersionUID = -5609061774548242181L; private String fileName;//文件名称 public void setFileName(String fileName) { if (ServletActionContext.getRequest().getMethod().equals("GET")) { try { byte[] bytes = fileName.getBytes("ISO8859-1");//将get方式提交的中文进行处理,即将编码由ISO8859-1转为utf-8 fileName = new String(bytes, "utf-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } this.fileName = fileName; } // 下载 public String execute() throws Exception { // 1.传统下载方式 // 取得HttpServletResponse对象 HttpServletResponse response = ServletActionContext.getResponse(); // 取得ServletContext对象 ServletContext context = ServletActionContext.getServletContext(); // 通知浏览器以下载方式打开文件 response.setHeader("content-disposition", "attachment;filename=" + URLEncoder.encode(fileName, "utf-8")); // 取得需要下载文件的根目录 String realPath = context.getRealPath("/WEB-INF/download"); // 构造子节输入流 InputStream is = new FileInputStream(realPath + "/" + fileName); // 构造子节输出流 OutputStream os = response.getOutputStream(); byte[] b = new byte[1024]; int len = 0; while ((len = is.read(b)) > 0) { os.write(b, 0, len); } is.close(); os.close(); return null; } }
download.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib uri='http://java.sun.com/jsp/jstl/core' prefix="c" %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> </head> <body> 英文需要编码 <a href="/struts2_learn/DownloadAction?fileName=building.jpg" style="text-decoration: none"> building.jpg </a> <hr> <%-- <c:url var="myURL" value="/DownloadAction"> <c:param name="fileName" value="建筑.jpg" /> </c:url> <a href="${myURL}" style="text-decoration: none"> 建筑.jpg </a> --%> </body> </html>
download_struts.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" "http://struts.apache.org/dtds/struts-2.3.dtd"> <struts> <package name="download" extends="struts-default"> <action name="DownloadAction" class="download.DownloadAction" method="execute"> </action> </package> </struts>
struts.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" "http://struts.apache.org/dtds/struts-2.3.dtd"> <struts> <!--<include file="config/upload.xml"></include> --> <!-- 加载其他配置文件 --> <!-- <include file="config/upload-interceptor.xml"></include> --> <!-- 加载属性文件-国际化 --> <!-- <constant name="struts.custom.i18n.resources" value="message"></constant> --> <!-- 结果集 --> <!-- <include file="config/result_struts.xml"></include> --> <!-- 类型转换 --> <!-- <include file="config/type_struts.xml"></include> --> <!-- 文件下载 --> <include file="config/download_struts.xml"></include> </struts>
效果如下图所示,点击building.jpg即可开始下载:
代码运行顺序:
首先,项目启动,加载struts.xml--->download_struts.xml-->将对应的DownloadAction加载.
其次,用户访问download.jsp页面,点击超链接,即开始调用DownloadAction进行下载.
核心程序即为DownloadAction.java,将要下载的文件写入到输入流,FileInputStream,然后再将文件从输入流写入到输出流,即将文件从一个地方拷贝到另一个地方.HttpServletResponse,将文件回写给浏览器,以达到下载文件的目的.
最后,文件成功下载,文件被拷贝到指定的地方.
这里要注意的是如果是GET请求方式,那么要注意中文乱码的问题,这里涉及到转码的问题,要做一个判断,详情可参考上面的代码.
2.使用stream方式进行文件下载:
DownloadStreamAction.java
package download; import java.io.InputStream; import java.net.URLEncoder; import org.apache.struts2.ServletActionContext; import com.opensymphony.xwork2.ActionSupport; /** * @ClassName: DownloadStreamAction * @Description: 现代下载方式 * @author: amosli * @email:amosli@infomorrow.com * @date Feb 13, 2014 12:35:26 AM */ public class DownloadStreamAction extends ActionSupport { private static final long serialVersionUID = -2747191035343710583L; private String fileName; public void setFileName(String fileName) throws Exception { if(ServletActionContext.getRequest().getMethod().equals("GET")){ byte[] bytes = fileName.getBytes("ISO8859-1"); fileName=new String(bytes,"utf-8"); } this.fileName = fileName; } public String getFileName() throws Exception { return URLEncoder.encode(fileName, "utf-8"); } public InputStream getImageStream() throws Exception { InputStream inputStream = ServletActionContext.getServletContext().getResourceAsStream("/WEB-INF/download/"+fileName); return inputStream; } }
download_struts.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" "http://struts.apache.org/dtds/struts-2.3.dtd"> <struts> <!-- <package name="download" extends="struts-default"> <action name="DownloadAction" class="download.DownloadAction" method="execute"> </action> </package> --> <package name="download" extends="struts-default"> <action name="DownloadAction" class="download.DownloadStreamAction" method="execute"> <!-- 以stream二进制流的方式打开 --> <result name="success" type="stream"> <!-- 指明文件的下载类型 --> <param name="contentType">image/jpeg</param> <!-- 指明如果取得需要下载文件的InputStream输入流 --> <param name="inputName">imageStream</param> <!-- 指明让浏览器以下载框的方式打开 --> <param name="contentDisposition">attachment;filename="building.jpg"</param> <!--<param name="contentDisposition">attachment;filename=${fileName}</param>--> <!-- 指明下载文件时的字符数组byte[]大小 --> <param name="bufferSize">1024</param> </result> </action> </package> </struts>
这里如果仅仅看DownloadStreamAction,会发现代码简洁明了,少了很多,再看download_struts.xml,会发现之前我们自己做的事,都被struts代替了,比如字符数组的大小,这里以参数配置的形式出现,方便了许多.
注意:imageStream要和DownloadStreamAction中的getImageStream相对应,不可写错.
关于其具体实现可以查看org.apache.struts2.dispatcher.StreamResult源码,其对应参数,看过源码后就会发现其实下载和传统方式差不多,就是将其优化了不少.