11.1、文件下载概述
1、将响应的内容类型设置为文件的内容类型。标头Content-type用来规定实体主体中的数据类型,包含媒体类型和子类型标识符。
2、添加一个名为Content-Disposition的HTTP响应头,给它赋值attachment;filename=filename,这里的fileName是指在文件下载对话框中显示出来的默认文件名。它通常与文件名相同,但是也可以不同。
例如,以下就是将一个文件发送到浏览器的代码范例。
FileInputStream fis = new FileInputStream(file) ;
BufferedInputStream bis = new BufferedInputStream(fis) ;
byte[] bytes = new byte[bis.available()] ;
response.setContentType(contentType) ;
OutputStream os = response.getOutputStream() ;
bis.read(bytes) ;
os.write(bytes) ;
警告:一定要确保你没有在无意中发送超出实际文件内容以外的任何字符。这有可能在你毫不知情的情况下发生。例如,如果需要在JSP页面中使用page指令,可以这么写:
<%@ page import=”Java.io.FileInputStream”%>
<jsp:useBean id=”DBBeanId” scope=”page” class=”MyBean”>
在你毫不察觉的情况下,page指令后面的回车换行符就会被发送给浏览器。为了防止发送多余的字符,需要像下面这样编写这个指令:
<%@ page import=”java.io.FileInputStream”
%><jsp:useBean id=”DBBeanId” scope=”page” class=”MyBean”>
11.2、范例1:隐藏资源
在下面这个程序中,我们用一个FileDownloadServlet servlet将secret.pdf文件发送到浏览器。但是,只有授权用户才能浏览。如果用户没有登录,应用程序就会跳转到Login页面。在这里,用户可以在表单中输入用户名和密码,这些内容都将被提交给另一个Servlet:LoginServlet。
LoginServlet.java
- package filedownloaded;
-
- import java.io.IOException;
-
- import javax.servlet.RequestDispatcher;
- import javax.servlet.ServletException;
- import javax.servlet.annotation.WebServlet;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import javax.servlet.http.HttpSession;
- @WebServlet(urlPatterns = {"/login"})
- public class LoginServlet extends HttpServlet{
-
- private static final long serialVersionUID = 1L;
-
- public void doPost(HttpServletRequest request,
- HttpServletResponse response) throws ServletException,
- IOException {
- 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");
- return ;
- }else{
- RequestDispatcher dispatcher =
- request.getRequestDispatcher("/login.jsp") ;
- dispatcher.forward(request, response);
- }
- }
-
- }
login.jsp
- <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
- <%
- String path = request.getContextPath();
- String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
- %>
-
- <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
- <html>
- <head>
- <base href="<%=basePath%>">
-
- <title>My JSP 'login.jsp' starting page</title>
-
- <meta http-equiv="pragma" content="no-cache">
- <meta http-equiv="cache-control" content="no-cache">
- <meta http-equiv="expires" content="0">
- <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
- <meta http-equiv="description" content="This is my page">
-
-
-
-
- </head>
-
- <body>
- <form action="login" method="post">
- <table>
- <tr>
- <td>User name: </td>
- <td><input name="userName"/></td>
- </tr>
- <tr>
- <td>Password: </td>
- <td><input name="password" type="password"/></td>
- </tr>
- <tr>
- <td colspan="2">
- <input type="submit" value="login"/>
- </td>
- </tr>
- </table>
- </form>
- </body>
- </html>
FileDownloadServlet.java
- package filedownloaded;
-
- 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.annotation.WebServlet;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import javax.servlet.http.HttpSession;
- @WebServlet(urlPatterns = {"/download"})
- public class FileDownloadServlet extends HttpServlet{
-
- private static final long serialVersionUID = 1L;
-
- @Override
- public void doGet(HttpServletRequest req, HttpServletResponse resp)
- throws ServletException, IOException {
- HttpSession session = req.getSession() ;
- if(session == null || session.getAttribute("loggedIn") == null){
- RequestDispatcher dispatcher = req.getRequestDispatcher("/login.jsp") ;
- dispatcher.forward(req, resp);
- return ;
- }
- String dataDirectory = req.getServletContext().getRealPath("/WEB-INF/data") ;
- File file = new File(dataDirectory, "secret.pdf") ;
- if(file.exists()){
- resp.setContentType("application/pdf");
- resp.addHeader("Content-Disposition", "attachment; filename=secret.pdf");
- byte[] buffer = new byte[1024] ;
- try(FileInputStream fis = new FileInputStream(file) ;
- BufferedInputStream bis = new BufferedInputStream(fis);
- OutputStream os = resp.getOutputStream()){
- int i = bis.read(buffer) ;
- while(i != -1){
- os.write(buffer, 0, i);
- i = bis.read(buffer) ;
- }
- }catch(IOException e){
- e.printStackTrace();
- }
- }
- }
-
- }
12.3、范例2:防止跨站引用
竞争对手很可能试图通过跨站引用来“窃取”你的网络资产,例如将你的贵重物品显示在他们的网站上,好像哪些东西就是他们的一样,如果通过编程的方式,仅当referer标头中包含你的域名时才发送资源,那么就可以防止上述情况的发生。当然,那种意志坚定的窃贼还是有可能下载到你的资产,但是那就要费一番功夫了。
下面的应用使用了一个Servlet,当且仅当referer标头不为空时,才将图片发送到浏览器。这样就可以防止直接在浏览器中输入其网址就能下载到图片。
ImageServlet.java
- package filedownloaded;
-
- import java.io.BufferedInputStream;
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.IOException;
- import java.io.OutputStream;
-
- import javax.servlet.ServletException;
- import javax.servlet.annotation.WebServlet;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- @WebServlet(urlPatterns = {"/getImage"})
- public class ImageServlet extends HttpServlet{
-
- private static final long serialVersionUID = 1L;
-
- public void doGet(HttpServletRequest request,
- HttpServletResponse response) throws ServletException,
- IOException{
- String referer = request.getHeader("referer") ;
- if(referer != null){
- String imageId = request.getParameter("id") ;
- String imageDirectory = request.getServletContext().getRealPath("/WEB-INF/image") ;
- File file = new File(imageDirectory, imageId + ".jpg") ;
- if(file.exists()){
- response.setContentType("image/jpg");
- byte[] buffer = new byte[1024] ;
- try(FileInputStream fis = new FileInputStream(file) ;
- BufferedInputStream 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){
- e.printStackTrace();
- }
- }
- }
- }
-
- }
images.html
- <!DOCTYPE html>
- <html>
- <head>
- <title>images.html</title>
-
- <meta name="keywords" content="keyword1,keyword2,keyword3">
- <meta name="description" content="this is my page">
- <meta name="content-type" content="text/html; charset=UTF-8">
-
-
-
- </head>
-
- <body>
- <img src="getImage?id=1"/>
- <img src="getImage?id=2"/>
- <img src="getImage?id=3"/>
- <img src="getImage?id=4"/>
- <img src="getImage?id=5"/>
- <img src="getImage?id=6"/>
- <img src="getImage?id=7"/>
- <img src="getImage?id=8"/>
- <img src="getImage?id=9"/>
- <img src="getImage?id=10"/>
- </body>
- </html>
这样就能避免其他网站使用爬虫等技术进行图片下载。直接复制图片地址是访问不到图片的。