您的位置:首页 > 其它

通过Servlet实现文件的下载

2013-04-22 18:11 399 查看
  当我们实现一个文件下载功能时,大多数人是通过Strust等框架实现的。Strust框架把底层的文件下载细节隐藏了起来,使我们不得其要领。下面我通过一个程序示例来再现通过Servlet下载文件的细节和原理。

程序源码

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>

<!--可以配置一些Listener 或 Filter 来观察一些对象的生命周期 -->

<servlet>
<servlet-name>download_servlet</servlet-name>
<servlet-class>edu.shao.webapp.sample.DownloadServlet</servlet-class>
<init-param>
<param-name>fileRoot</param-name>
<param-value>d:/</param-value>
</init-param>
<init-param>
<param-name>contentType</param-name>
<param-value>application/octet-stream</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>download_servlet</servlet-name>
<url-pattern>/download</url-pattern>
</servlet-mapping>
</web-app>


DownloadServlet.java

package edu.shao.webapp.sample;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URLEncoder;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class DownloadServlet extends HttpServlet{
private static final long serialVersionUID = 1L;
public static Logger logger=LogManager.getLogger(DownloadServlet.class);

private String contentType;
private String enc="UTF-8";
private String fileRoot;

@Override
public void init(ServletConfig config) throws ServletException {
contentType = config.getInitParameter("contentType");
fileRoot = config.getInitParameter("fileRoot");
}

@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
logger.debug("Do Get Method.");
String fileName=req.getParameter("fileName");
String filePath=fileRoot+File.separator+fileName;

File downloadFile=new File(filePath);
if (downloadFile.exists()) {
logger.info("File exist");

resp.setContentType(contentType);
Long length=downloadFile.length();
resp.setContentLength(length.intValue());
fileName = URLEncoder.encode(downloadFile.getName(), enc);
resp.addHeader("Content-Disposition", "attachment; filename=" + fileName);

ServletOutputStream servletOutputStream=resp.getOutputStream();
FileInputStream fileInputStream=new FileInputStream(downloadFile);
BufferedInputStream bufferedInputStream=new BufferedInputStream(fileInputStream);
int size=0;
byte[] b=new byte[4096];
while ((size=bufferedInputStream.read(b))!=-1) {
logger.info("write to output stream..");
servletOutputStream.write(b, 0, size);
}
servletOutputStream.flush();
servletOutputStream.close();
bufferedInputStream.close();
}else {
logger.info("File is not exist");
}
}

}


解释:

1、contentType用于定义用户的浏览器如何显示将要加载的数据,或者如何处理将要加载的数据。如网页的contentType是text/html,jpeg图片的contentType是image/jpeg。如果不知道文件类型,可以设置为二进制流文件 application/octet-stream

2、contentType和fileRoot(待下载文件的目录)通过web.xml配置。

3、resp.setContentType()、resp.setContentLength()、resp.addHeader("Content-Disposition", "attachment; filename=" + fileName); 三个调用设置一些必要的响应报头(reponse header)。

运行结果:

1、浏览器地址栏输入:http://localhost:8080/test/download?fileName=test.mkv  回车(文件“d:/test.mkv”的大小为700M)

2、浏览器弹出下载提示窗口,但未点击保存。控制台先打印几条日志,然后停滞,说明已经向输出流写入了4kB左右(程序中byte数组的大小为4096B)的数据了。



3、选择保存位置后,点击保存。此时控制台开始疯狂地输出log。。。

4、最后下载完成,此次Request结束。



小结:

  文件下载的原理非常简单,就是把数据从一个输入流中读出数据,再写入一个输出流。这里的输入流是FileInputStream(为了提高速度,对其包装了一个装饰类BufferedInputStream,以提供缓冲功能),输出流是ServletOutputStream。

  源码中的定义,public abstract class ServletOutputStream extends OutputStream。说明ServletOutputStream继承了OutputStream,它和FileOutputStream等输出流是一样的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: