Java Web实现文件下载的几种方式

文件下载可以说是网站的基础功能,要实现最下载功能,有一种最基本的方法,那就是将超链接的href属性指向对应的资源文件。

如下面连接指向了百度首页的图片:

I'm the index of Baidu

但这种方式的缺陷也是很明显的,目录信息被获取,不利于信息安全。其实信息安全还是其次,主要还是因为它不方便。如果直接指向资源文件,那么浏览器会自动打开图片等一些文件,而不会弹出窗口提示用户保存。

因此这种方式并不能很好地解决下载的问题。因此就有了下面的几种下载方式。

而在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>
View Code

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 }
View Code

这个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>
View Code

二、在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>
View Code

其中<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 }
View Code
posted @ 2014-07-16 14:38  陈树义  阅读(1299)  评论(0编辑  收藏  举报