Servlet学习笔记(九)—— 文件下载

一、文件下载概述

例如图片或者HTML这类静态资源,只要在浏览器中打开正确的网址就可以下载。只要资源放在应用程序目录或者其下的子目录中,但不在WEB-INF下,Servlet/JSP容器就会将资源发送到浏览器。但有的时候,静态资源被保存在应用程序目录之外,或者保存在数据库中,或者有时候你需要控制让某些人能够看到这个资源,同时又要防止其他网站引用它。每当遇到这类情况时,就必须通过编程来发送资源。

通过编程的方式实现文件下载可是让我们有选择的将一个文件发送到浏览器。

为了将资源比如文件发送到浏览器,需要在Servlet中完成以下工作。这里说明以下,一般不用JSP页面,因为要发送的是浏览器不会显示的二进制代码。

1.将响应内容类型设置为“文件”的内容类型。

标头Content-Type用来规定实体主体中的数据类型,包含“媒体类型”和“子类型标示符”。如果希望浏览器总是显示为“Save as”对话框时,就将内容类型设置为:application/octetstream。

2.添加一个名为content-disposition的HTTP响应标头。

给上述响应标头赋值:attachment;filename="XXX"。这里的XXX是在文件下载对话框中显示的默认文件名。他可以与真实的文件名相同,也可以不同。

下面是一个文件下载的实例概述:

response.setContentType(contenttype);
FileInputStream fis=new FileInputStream(file);
BufferedInputStream bis=new BufferedInputStream(fis);
byte[] bytes=new byte[1024];
OutputStream os=response.getOutputStream();
bis.read(bytes);
os.write(bytes);

首先,将要下载的文件当成一个FileInputStream,并将内容添加到一个字节数组中;

然后获取HttpServletResponse的OutputStream,并调用它的write()方法,将字节数组传递给它。

二、下载隐藏文件实例

在下面这个示例中,secret.pdf文件放在WEB-INF/data里,不允许直接访问。用一个FileDownloadServlet将secret.pdf文件发送到浏览器,但是授权用户才能浏览。如果用户没有登录,应用程序就会跳转到login界面,这里,用户可以在表单中输入用户名和密码,这些内容都被提交到另一个LoginServlet处理。

下面是该应用程序的结构图:

1.用户提交登录表单

login.jsp页面,包含一个HTML表单,其中有两个输入域:userName和password

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<!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=ISO-8859-1">
<title>Login</title>
</head>
<body>
<form action="login" method="post">
	<table>
		<tr>
			<td>User Name:</td>
			<td><input type="text" name="userName"/></td>
		</tr>
		<tr>
			<td>PassWord:</td>
			<td><input type="password" name="password"/></td>
		</tr>
		<tr>
			<td colspan="2"><input type="submit" value="login"/></td>
		</tr>
	</table>
</form>
</body>
</html>

提交表单时会调用LoginServlet,它的类代码如下。在这个应用程序中,我假设用户名和密码必须是ken/secret。

package filedownload;

import java.io.IOException;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

/**
 * Servlet implementation class LoginServlet
 */
public class LoginServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

    /**
     * @see HttpServlet#HttpServlet()
     */
    public LoginServlet() {
        super();
        // TODO Auto-generated constructor stub
    }

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		String username=request.getParameter("userName");
		String password=request.getParameter("password");
		if(username!=null&&username.equals("ken")&&password!=null&&password.equals("secret")){
			HttpSession session=request.getSession(true);
			session.setAttribute("loggedIn", Boolean.TRUE);
			response.sendRedirect("download");
                        //must call return or else the code after this if
                        //block,if any,will be executed
                        return;
		}else{
			RequestDispatcher dispatcher=request.getRequestDispatcher("/login.jsp");
			dispatcher.forward(request, response);
		}
	}

}<span style="font-size:14px;">
</span>

用户登录成功后,会设置一个loggedln会话属性,并将用户转到FileDownloadServlet。

在HttpServletResponse.sendRedirect之后,必须返回,以防止执行后面的代码行。登录失败后,则会将用户转到login.jsp页面。

2.进行文件下载

FileDownloadServlet展示了一个负责发送secret.pdf文件的Servlet。只有当用户的HttpSession中包含loggedlin属性时,表示用户已经成功登录,此时才允许访问。

package filedownload;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

/**
 * Servlet implementation class FileDownloadServlet
 */
public class FileDownloadServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

    /**
     * @see HttpServlet#HttpServlet()
     */
    public FileDownloadServlet() {
        super();
        // TODO Auto-generated constructor stub
    }

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		HttpSession session=request.getSession();
		if(session==null||session.getAttribute("loggedIn")==null){
			RequestDispatcher dispatcher=request.getRequestDispatcher("/login.jsp");
			dispatcher.forward(request, response);
			return;//must return after dispatcher.forward(),Otherwise the code below will be executed
		}
		String dataDirectory =request.getServletContext().getRealPath("/WEB-INF/data/secret.pdf");
		//System.out.println(dataDirectory);
		File file=new File(dataDirectory);
		if(file.exists()){
			response.setContentType("application/pdf");
			response.addHeader("Content-Disposition", "attachment;filename=secret.pdf");
			byte[] buffer=new byte[1024];
			FileInputStream fis=null;
			BufferedInputStream bis=null;
			try{
				fis=new FileInputStream(file);
				bis=new BufferedInputStream(fis);
				OutputStream os=response.getOutputStream();
				int i=bis.read(buffer);
				while(i!=-1){
					os.write(buffer,0,i);
					i=bis.read(buffer);
				}
			}catch(IOException e){
				System.out.println(e.toString());
			}finally{
				if(bis!=null){
					bis.close();
				}
				if(fis!=null){
					fis.close();
				}
			}
		}else{
			System.out.println("secret.pdf does not exit!");
		}
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
	}

}

doGet方法会检查HttpSession中是否有loggedIn属性。如果没有,则会将用户带到登录界面。

HttpSession session=request.getSession();
		if(session==null||session.getAttribute("loggedIn")==null){
			RequestDispatcher dispatcher=request.getRequestDispatcher("/login.jsp");
			dispatcher.forward(request, response);
			return;//must return after dispatcher.forward(),Otherwise the code below will be executed
		}

注意,在RequestDispatcher中调用forward会将控制权转移到不同的资源。但是,它不会中止当前在调用对象的代码执行,因此,跳转之后必须返回。

如果用户已经登录成功,doGet方法就会打开索要的资源,并将它引到ServletResponse的OutputStream。

String dataDirectory =request.getServletContext().getRealPath("/WEB-INF/data/secret.pdf");
		//System.out.println(dataDirectory);
		File file=new File(dataDirectory);
		if(file.exists()){
			response.setContentType("application/pdf");
			response.addHeader("Content-Disposition", "attachment;filename=secret.pdf");
			byte[] buffer=new byte[1024];
			FileInputStream fis=null;
			BufferedInputStream bis=null;
			try{
				fis=new FileInputStream(file);
				bis=new BufferedInputStream(fis);
				OutputStream os=response.getOutputStream();
				int i=bis.read(buffer);
				while(i!=-1){
					os.write(buffer,0,i);
					i=bis.read(buffer);
				}
			}catch(IOException e){
				System.out.println(e.toString());
			}finally{
				if(bis!=null){
					bis.close();
				}
				if(fis!=null){
					fis.close();
				}
			}
		}else{
			System.out.println("secret.pdf does not exit!");
		}

利用下面的URL调用FileDownloadServlet,可以对该应用程序进行测试:

http://localhost:8080/app12a/download

3.PS:web.xml配置文件

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
  <display-name>app12a</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
  <servlet>
    <description></description>
    <display-name>FileDownloadServlet</display-name>
    <servlet-name>FileDownloadServlet</servlet-name>
    <servlet-class>filedownload.FileDownloadServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>FileDownloadServlet</servlet-name>
    <url-pattern>/download</url-pattern>
  </servlet-mapping>
  <servlet>
    <description></description>
    <display-name>LoginServlet</display-name>
    <servlet-name>LoginServlet</servlet-name>
    <servlet-class>filedownload.LoginServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>LoginServlet</servlet-name>
    <url-pattern>/login</url-pattern>
  </servlet-mapping>
</web-app>

4.测试截图

摘录自:《Servlet和JSP学习指南》

时间: 2024-10-06 05:04:02

Servlet学习笔记(九)—— 文件下载的相关文章

SERVLET 学习笔记

SERVLET 学习笔记 一.Servlet基本定义 Servlet是服务器端上面运行的一段小的java程序,一个servlet就是一个简答的java类.通常servlet都是通过请求和返回的模式来被访问的,客户端通过resuest请求,servlet则通过response来返回需要的内容. 二.Tomcat容器等级 Tomcat容器等级分为四个等级,由内向外分别是:context容器àSERVLET容器àHOST(主机)容器àENGINE(引擎)容器.其中,CONTEXT容器,一个CONTEX

JavaWeb 后端 &lt;二&gt; 之 Servlet 学习笔记

JavaWeb 后端 <二> 之 Servlet 学习笔记 一.Servlet概述 1.什么是Servlet Servlet是一个运行在服务器端的Java小程序,通过HTTP协议用于接收来自客户端请求,并发出响应. 2.Servlet中的方法 public void service(ServletRequest req,ServletResponse res) throws ServletException,java.io.IOException ServletRequest req:代表着请

测试servlet学习笔记

操作方法: 1.新建工程: File-->new-->Java Project-->TestServlet(工程名称)-->Finish. 2.加载servlet-api.jar类包: TestServlet(右键)-->Build Path-->Configure Build Path-->Library -->Add External JAR Selection-->(浏览在tomcat的lib目录下找到servlet-api.jar选中后点击打开

Servlet学习笔记(八)—— 自定义过滤器的编写改进:自定义实现FilterChain

笔记六中实现了三种过滤器:字符编码过滤.登录权限过滤.敏感词过滤,但是有个缺陷就是,限定了过滤顺序,而不能实现先进行request过滤,最后response过滤,并且中间几项过滤的顺序不能动态改变.所以这里做个改进,实现一个过滤顺序的FilterChain. 多个Filter的执行顺序在这篇博文中得到很仔细的讲解,总结一点,多个过滤器的执行顺序是根据web.xml中不同<filter-mapping>的顺序来先后执行的,比如: <?xml version="1.0"

APUE 学习笔记(九) 高级I/O

1. 非阻塞I/O 低速系统调用时可能会使进程永远阻塞的一类系统调用,包括以下调用: (1)某些文件类型你(网络socket套接字.终端设备.管道)暂无可使用数据,则读操作可能会使调用者永远阻塞 (2)如果数据不能立即被(1)中文件类型接受,则写操作会使调用者永远阻塞 (3)某些进程间通信函数 非阻塞I/O使我们可以调用open.read.write这样的I/O操作,并使这些操作不会永远阻塞,如果这种操作不能完成,则调用立即出错返回 对于一个给定的文件有两种方法对其指定非阻塞I/O: (1)调用

python学习笔记九——文件与目录

1.python进行文件读写的函数是open或file类 mode:r  只读 r+   读写 w  写入,先删除原文件,再重新写入,如果文件没有则创建 w+  读写,先删除原文件,再重新写入,如果文件没有则创建(可写入和输出) a  写入,在文件末尾追加新的内容,文件不存在则创建 a+  读写,在文件末尾追加新的内容,文件不存在则创建 b  打开二进制文件,可与r,w,a,+结合使用 U  支持所有的换行符号,"\r","\n","\r\n"

angular学习笔记(九)-css类和样式3

再来看一个选择li列表的例子: 点击li中的任意项,被点击的li高亮显示: <!DOCTYPE html> <html ng-app> <head> <title>6.3css类和样式</title> <meta charset="utf-8"> <script src="../angular.js"></script> <script src="scri

angular学习笔记(九)-css类和样式2

在上一个例子中,元素的类名使用拼接的方法,这样,类名中就不得不带有true或false,并且不易维护,所以,angular使用ng-class属性来控制元素的类名: 我们来看一个小例子,点击error按钮,顶部提示错误框,点击warning按钮,顶部提示警告框. 错误框的类名是.err,警告框的类名是.warn: <!DOCTYPE html> <html ng-app> <head> <title>6.2css类和样式</title> <

Linux System Programming 学习笔记(九) 内存管理

1. 进程地址空间 Linux中,进程并不是直接操作物理内存地址,而是每个进程关联一个虚拟地址空间 内存页是memory management unit (MMU) 可以管理的最小地址单元 机器的体系结构决定了内存页大小,32位系统通常是 4KB, 64位系统通常是 8KB 内存页分为 valid or invalid: A valid page is associated with an actual page of data,例如RAM或者磁盘上的文件 An invalid page is

Servlet学习笔记(七)—— JSP概述

1.Servlet两个缺陷: ①所有HTML标签必须包在Java字符串中,使得发送HTTP响应十分繁琐: ②所有文本和HTML标签都必须进行硬编码,即使对表示层做极其微小的修改,也需要重新编译. 2.注释: (1)JSP备注:<% %>,不会发送到浏览器,不能嵌套 (2)HTML备注:<!-- -->,不被容器处理,直接发送到浏览器,用途之一是标识JSP页面.在处理带有许多JSP片段的应用程序时,开发者通过查看HTML源代码,可以轻松查出某个HTML代码部分生成了那个JSP页面.