Java Web实现文件下载的几种方式
文件下载可以说是网站的基础功能,要实现最下载功能,有一种最基本的方法,那就是将超链接的href属性指向对应的资源文件。
如下面连接指向了百度首页的图片:
但这种方式的缺陷也是很明显的,目录信息被获取,不利于信息安全。其实信息安全还是其次,主要还是因为它不方便。如果直接指向资源文件,那么浏览器会自动打开图片等一些文件,而不会弹出窗口提示用户保存。
因此这种方式并不能很好地解决下载的问题。因此就有了下面的几种下载方式。
而在Java中,要实现下载功能一般有三种实现方式:
1、使用Servlet实现文件下载
2、在Struts中用Servlet实现下载
3、使用Struts框架提供的文件下载功能
其实这三种实现方式的原理都一样,都是利用InputStream从文件中读取数据,然后利用OutputStream将数据接入到返回客户端的response中。
其核心代码如下:
1 try { 2 InputStream inStream = new FileInputStream(file); 3 ServletOutputStream servletOS = response.getOutputStream(); 4 byte[] buf = new byte[4096]; 5 int readLength; 6 while((readLength = inStream.read(buf))!= -1) 7 { 8 servletOS.write(buf, 0, readLength); 9 } 10 inStream.close(); 11 servletOS.flush(); 12 servletOS.close(); 13 } 14 catch(Exception e) 15 { 16 e.printStackTrace(); 17 } 18 return response;
其中第一种在Servlet中实现文件下载是最原始的下载方式。而第二种在Struts中用Servlet实现文件下载也是最原始的方式,只不过我们是在Struts的Action中进行操作,不用去创建Servlet。
而第三种则是Struts对文件下载功能进行了封装,我们只要按照其规定的配置就可以直接使用。
这几种方式各有各的优缺点,选择适合自己的方式即可。下面对上面提到的三种方式进行详细解析:
一、使用Servlet实现文件下载
这种方式性能最好,条理最清晰,但是可能代码量多了点。
准备:只需Java的JDK即可运行。
1、在web.xml中进行Servlet配置
1 <?xml version="1.0" encoding="UTF-8"?> 2 <web-app version="2.5" 3 xmlns="http://java.sun.com/xml/ns/javaee" 4 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 5 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 6 http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> 7 8 <!-- 文件下载 --> 9 <servlet> 10 <servlet-name>fileDownLoadServlet</servlet-name> 11 <servlet-class>com.chanshuyi.download.FileDownLoadServlet</servlet-class> 12 </servlet> 13 <servlet-mapping> 14 <servlet-name>fileDownLoadServlet</servlet-name> 15 <url-pattern>/fileDownLoadServlet</url-pattern> 16 </servlet-mapping> 17 </web-app>
2、创建Servlet类
1 package com.chanshuyi.download; 2 3 import java.io.File; 4 import java.io.FileInputStream; 5 import java.io.IOException; 6 import java.io.InputStream; 7 import java.io.UnsupportedEncodingException; 8 import java.net.URLEncoder; 9 10 import javax.servlet.ServletException; 11 import javax.servlet.ServletOutputStream; 12 import javax.servlet.http.HttpServlet; 13 import javax.servlet.http.HttpServletRequest; 14 import javax.servlet.http.HttpServletResponse; 15 16 /** 17 * 文件下载Servlet 18 * @param filePath 下载文件相对于项目根目录的相对地址</P>如:uploaded\hello.txt 19 * @author chenyr 20 * 21 */ 22 @SuppressWarnings("serial") 23 public class FileDownLoadServlet extends HttpServlet { 24 25 /** 项目的根目录</P>如:D:\Workspaces\DownUpProject\WebRoot\ **/ 26 private String rootPath; 27 28 /** 下载文件的相对于项目根目录的相对路径 **/ 29 private String filePath; 30 31 /** 下载文件在磁盘的绝对路径 **/ 32 private String fullPath; 33 34 /** 下载文件名 **/ 35 private String fileName; 36 37 /** 编码过后的下载文件名 **/ 38 private String encodeFileName; 39 40 /** 41 * 自定义init,用于初始化下载所属属性 42 * @param request 43 * @return 44 * @throws UnsupportedEncodingException 45 */ 46 public boolean myInit(HttpServletRequest request) 47 { 48 boolean flag = false; 49 try 50 { 51 setRootPath(this.getServletContext().getRealPath("/")); 52 setFilePath(new String(request.getParameter("filePath").getBytes("iso-8859-1"), "utf-8")); 53 setFullPath(getRootPath() + getFilePath()); 54 55 setFileName(getFilePath().substring(getFilePath().lastIndexOf("\\") + 1)); 56 setEncodeFileName(URLEncoder.encode(getFileName(), "utf-8")); //支持中文文件名 57 58 flag = true; 59 } 60 catch(Exception e) 61 { 62 e.printStackTrace(); 63 } 64 return flag; 65 } 66 67 /** 68 * Servlet类的服务方法 69 */ 70 @Override 71 protected void service(HttpServletRequest request, 72 HttpServletResponse response) throws ServletException, IOException { 73 download(request, response); 74 } 75 76 /** 77 * 文件下载 78 * @param path 79 * @param request 80 * @param response 81 * @return 82 * @throws UnsupportedEncodingException 83 */ 84 public HttpServletResponse download(HttpServletRequest request, HttpServletResponse response){ 85 /* 初始化相关属性信息 */ 86 myInit(request); 87 88 /* 检查文件是否存在 */ 89 File file = new File(getFullPath()); 90 if(!file.exists()) 91 { 92 System.out.println("文件不存在:" + file.getAbsolutePath()); 93 return null; 94 } 95 96 /* 设置response头信息 */ 97 response.reset(); 98 response.setContentType("application/octet-stream"); //代表任意二进制数据 99 response.addHeader("Content-Disposition", "attachment;filename=\"" + getEncodeFileName() + "\""); 100 response.setContentLength((int)file.length()); 101 102 /* 读取文件数据流 */ 103 if(file.exists()) 104 { 105 /* 读取文件数据 */ 106 if((int)file.length() != 0) 107 { 108 try 109 { 110 InputStream inStream = new FileInputStream(file); 111 112 ServletOutputStream servletOS = response.getOutputStream(); 113 byte[] buf = new byte[4096]; 114 int readLength; 115 while((readLength = inStream.read(buf))!= -1) 116 { 117 servletOS.write(buf, 0, readLength); 118 } 119 120 inStream.close(); 121 servletOS.flush(); 122 servletOS.close(); 123 } 124 catch(Exception e) 125 { 126 e.printStackTrace(); 127 } 128 } 129 } 130 return response; 131 } 132 133 /* 134 * GET/SET 135 */ 136 public String getFilePath() { 137 return filePath; 138 } 139 140 public void setFilePath(String filePath) { 141 this.filePath = filePath; 142 } 143 144 public String getFullPath() { 145 return fullPath; 146 } 147 148 public void setFullPath(String fullPath) { 149 this.fullPath = fullPath; 150 } 151 152 public String getFileName() { 153 return fileName; 154 } 155 156 public void setFileName(String fileName) { 157 this.fileName = fileName; 158 } 159 160 public String getEncodeFileName() { 161 return encodeFileName; 162 } 163 164 public void setEncodeFileName(String encodeFileName) { 165 this.encodeFileName = encodeFileName; 166 } 167 168 public String getRootPath() { 169 return rootPath; 170 } 171 172 public void setRootPath(String rootPath) { 173 this.rootPath = rootPath; 174 } 175 }
这个Servlet类对下载功能进行很好的封装,只需要传入名字为:filePath的参数就可以进行下载(filePath是下载文件相对于根目录[WEBROOT目录]的相对路径)。
98行设置response的contentType为application/octet-stream,可以实现任意类型文件的下载。
56行设置URLEncoder.encode对文件名进行编码转换,实现对中文文件名的下载支持。
3、创建访问页面测试
1 <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> 2 <% 3 String path = request.getContextPath(); 4 String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; 5 %> 6 7 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> 8 <html> 9 <head> 10 <base href="<%=basePath%>"> 11 12 <title>My JSP 'index.jsp' starting page</title> 13 <meta http-equiv="pragma" content="no-cache"> 14 <meta http-equiv="cache-control" content="no-cache"> 15 <meta http-equiv="expires" content="0"> 16 <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> 17 <meta http-equiv="description" content="This is my page"> 18 <!-- 19 <link rel="stylesheet" type="text/css" href="styles.css"> 20 --> 21 </head> 22 23 <body> 24 <a href="fileDownLoadServlet?filePath=uploaded\通讯录.xls">哈哈,测试文件下载</a> 25 </body> 26 </html>
二、在Struts中用Servlet实现下载
这种方式其实就是将数据流写入response中,其实现方式与第一种完全相同。不同的是在Servlet类中,我们可以直接得到request、response、ServletContext,但是在Struts的Action中我们必须通过其他方式得到这几个变量。
下面的代码实在第一种实现方式的代码的基础上进行少量修改而成的,它实现了在Struts Action中下载的功能:
package com.chanshuyi.download; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import javax.servlet.ServletContext; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.struts2.ServletActionContext; import com.opensymphony.xwork2.ActionSupport; @SuppressWarnings("serial") public class FileDownloadViaServlet extends ActionSupport { HttpServletRequest request = ServletActionContext.getRequest(); HttpServletResponse response = ServletActionContext.getResponse(); ServletContext servletContext = ServletActionContext.getServletContext(); /** 项目的根目录</P>如:D:\Workspaces\DownUpProject\WebRoot\ **/ private String rootPath; /** 下载文件的相对于项目根目录的相对路径 **/ private String filePath; /** 下载文件在磁盘的绝对路径 **/ private String fullPath; /** 下载文件名 **/ private String fileName; /** 编码过后的下载文件名 **/ private String encodeFileName; /** * 自定义init,用于初始化下载所属属性 * @param request * @return * @throws UnsupportedEncodingException */ public boolean myInit() { boolean flag = false; try { setRootPath(servletContext.getRealPath("/")); setFilePath(new String(request.getParameter("filePath").getBytes("iso-8859-1"), "utf-8")); setFullPath(getRootPath() + getFilePath()); setFileName(getFilePath().substring(getFilePath().lastIndexOf("\\") + 1)); setEncodeFileName(URLEncoder.encode(getFileName(), "utf-8")); //支持中文文件名 flag = true; } catch(Exception e) { e.printStackTrace(); } return flag; } /** * Action执行方法 */ @Override public String execute() { download(); return null; } /** * 文件下载 * @param path * @param request * @param response * @return * @throws UnsupportedEncodingException */ public HttpServletResponse download(){ /* 初始化相关属性信息 */ myInit(); /* 检查文件是否存在 */ File file = new File(getFullPath()); if(!file.exists()) { System.out.println("文件不存在:" + file.getAbsolutePath()); return null; } /* 设置response头信息 */ response.reset(); response.setContentType("application/octet-stream"); //代表任意二进制数据 response.addHeader("Content-Disposition", "attachment;filename=\"" + getEncodeFileName() + "\""); response.setContentLength((int)file.length()); /* 读取文件数据流 */ if(file.exists()) { /* 读取文件数据 */ if((int)file.length() != 0) { try { InputStream inStream = new FileInputStream(file); ServletOutputStream servletOS = response.getOutputStream(); byte[] buf = new byte[4096]; int readLength; while((readLength = inStream.read(buf))!= -1) { servletOS.write(buf, 0, readLength); } inStream.close(); servletOS.flush(); servletOS.close(); } catch(Exception e) { e.printStackTrace(); } } } return response; } /* * GET/SET */ public String getFilePath() { return filePath; } public void setFilePath(String filePath) { this.filePath = filePath; } public String getFullPath() { return fullPath; } public void setFullPath(String fullPath) { this.fullPath = fullPath; } public String getFileName() { return fileName; } public void setFileName(String fileName) { this.fileName = fileName; } public String getEncodeFileName() { return encodeFileName; } public void setEncodeFileName(String encodeFileName) { this.encodeFileName = encodeFileName; } public String getRootPath() { return rootPath; } public void setRootPath(String rootPath) { this.rootPath = rootPath; } }
三、使用Struts框架提供的文件下载功能
通过Struts封装的方式实现文件下载,我们需要在Struts配置文件中进行Response HTTP头等信息的设置,以及建立对应的Action。
准备:导入Struts所需的Jar包,并搭建好Struts环境。不清楚的,可以点这里:Struts框架配置详解
1、在Struts中添加Action
1 <!-- 文件下载 --> 2 <action name="fileDownload" class="com.chanshuyi.download.FileDownLoadAction" method="download"> 3 <!-- 初始文件名 --> 4 <param name="fileName">佛山“狠刹”四风网络监督平台短信责任人填报表V1.0.xls</param> 5 <result name="success" type="stream"> 6 <param name="contentType">application/vnd.ms-excel</param> 7 <param name="inputName">mbInputStream</param> 8 <param name="contentDisposition">attachment;filename=${downloadFileName}</param> 9 <param name="bufferSize">4096</param> 10 </result> 11 </action>
其中<param name="inputName">mbInputStream</param>中的mbInputStream对应Action中类型为InputStream的属性。
2、创建Action类
1 package com.chanshuyi.download; 2 3 import java.io.FileNotFoundException; 4 import java.io.InputStream; 5 import java.io.UnsupportedEncodingException; 6 import java.net.URLEncoder; 7 8 import javax.servlet.http.HttpServletRequest; 9 10 import org.apache.struts2.ServletActionContext; 11 12 import com.opensymphony.xwork2.ActionSupport; 13 14 public class FileDownLoadAction extends ActionSupport { 15 16 /** 项目的根目录</P>如:D:\Workspaces\DownUpProject\WebRoot\ **/ 17 private String rootPath; 18 19 /** 下载文件的相对于项目根目录的相对路径 **/ 20 private String filePath; 21 22 /** 下载文件在磁盘的绝对路径 **/ 23 private String fullPath; 24 25 /** 下载文件名 **/ 26 private String fileName; 27 28 /** 编码过后的下载文件名 **/ 29 private String encodeFileName; 30 31 32 /** 对应于配置文件的输入流 **/ 33 private InputStream inputStream; 34 35 public String getRootPath() { 36 return rootPath; 37 } 38 39 public void setRootPath(String rootPath) { 40 this.rootPath = rootPath; 41 } 42 43 public String getFilePath() { 44 return filePath; 45 } 46 47 public void setFilePath(String filePath) { 48 this.filePath = filePath; 49 } 50 51 public String getFullPath() { 52 return fullPath; 53 } 54 55 public void setFullPath(String fullPath) { 56 this.fullPath = fullPath; 57 } 58 59 public String getFileName() { 60 return fileName; 61 } 62 63 public void setFileName(String fileName) { 64 this.fileName = fileName; 65 } 66 67 public void setEncodeFileName(String encodeFileName) { 68 this.encodeFileName = encodeFileName; 69 } 70 71 /** 72 * 初始化参数 73 * @throws UnsupportedEncodingException 74 */ 75 public void myInit() throws UnsupportedEncodingException 76 { 77 HttpServletRequest request = ServletActionContext.getRequest(); 78 79 setRootPath(ServletActionContext.getServletContext().getRealPath("/")); 80 setFilePath(new String(request.getParameter("filePath").getBytes("iso-8859-1"), "utf-8")); 81 setFullPath(getRootPath() + getFilePath()); 82 83 setFileName(getFilePath().substring(getFilePath().lastIndexOf("\\") + 1)); 84 setEncodeFileName(URLEncoder.encode(getFileName(), "utf-8")); //支持中文文件名 85 } 86 87 /** 88 * 对应的Action方法 89 * @return 90 * @throws UnsupportedEncodingException 91 */ 92 public String download() throws UnsupportedEncodingException 93 { 94 myInit(); 95 return SUCCESS; 96 } 97 98 /** 99 * 对文件名进行编码转换,以支持中文文件名 100 * @return 101 */ 102 public String getEncodeFileName() { 103 return encodeFileName; 104 } 105 106 /** 107 * 获取下载文件的输入流 108 * @return 109 * @throws FileNotFoundException 110 */ 111 public InputStream getInputStream() throws FileNotFoundException { 112 return new java.io.FileInputStream(getFullPath()); 113 } 114 }