• 概述
  • 文件下载概览
  • 隐藏资源
  • 防止交叉引用
  • 源码

概述

像静态资源,我们在浏览器中打开正确的URL即可下载,只要该资源不是放在WEB-INF目录下,Servlet/JSP容器就会将该资源发送到浏览器。 然而有的时候静态资源是保存在应用程序目录外或者存在数据库中,或者有的时候需要控制它的访问权限,防止其他网站交叉引用它。 如果出现上述任意一种情况,都必须通过编程来发送资源。


文件下载概览

为了将像文件这样的资源发送到浏览器,需要在控制器中完成以下工作

1. 队请求处理方法使用void返回类型,并在方法中添加HttpServletRespinse参数

2. 将响应的内容设置为文件的内容类型。 Content-Type标题在某个实体的body中定义数据的类型,并包含没提类型和子类型标示符。如果不清楚内容类型,并且希望浏览器始终显示Save As(另存为)对话框,则将它设置为APPLICATION/OCTETPSTREAM ,不区分大小写

3. 添加一个名为Content-Dispositionde HTTP响应标题,并赋值attachment;filename=fileName.这里的fileName是默认文件名,应该出现在File Download对话框中,它通常与文件名同名,但是也并非一定如此

下面的代码是将一个文件发送到浏览器

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

为了通过编程将一个文件发送到浏览器,首先要读取该文件作为FileInputStream,并将内容加载到一个字节数组。 随后,获取HttpServletResponse的OutputStream,并调用其write方法传入字节数组。


隐藏资源

该示例演示如何向浏览器发送文件,由ResourceController类处理用户登录请求,并将WEB-INF/data目录下的artisan.pdf发送给浏览器。因为文件放到了WEB-INF目录下,所以不能够直接访问,只有得到授权的用户才能看到,如果未登录,返回登录页面。

ResourceController, 这里模拟下用户登录,只有当用户的HttpSession中包含一个loggedIn属性时,表明登录成功。

package com.artisan.controller;import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.OutputStream;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;import com.artisan.domain.Login;@Controller
public class ResourceController {@RequestMapping(value = "/login")public String login(@ModelAttribute Login login, HttpSession httpSession, Model model) {model.addAttribute("login", new Login());if ("artisan".equals(login.getUserName()) && "artisan123".equals(login.getPassword())) {httpSession.setAttribute("loggedIn", Boolean.TRUE);return "Main";} else {return "LoginForm";}}@RequestMapping(value = "/resource_download")public String downLoadResource(HttpSession session, HttpServletRequest request, HttpServletResponse response) {if (session == null && session.getAttribute("loggedIn") == null) {return "LoginForm";}String dataDirectory = request.getServletContext().getRealPath("/WEB-INF/data");File file = new File(dataDirectory, "artisan.pdf");if (file.exists()) {response.setContentType("application/pdf");response.addHeader("Content-Disposition", "attachment; filename=artisan.pdf");byte[] buffer = new byte[1024];FileInputStream fis = null;BufferedInputStream bis = null;// JDK7 以前的写法// 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 ex) {// // do something,// // probably forward to an Error page// } finally {// if (bis != null) {// try {// bis.close();// } catch (IOException e) {// }// }// if (fis != null) {// try {// fis.close();// } catch (IOException e) {// }// }// }//// Java 7, use try-with-resources,自动释放资源try (OutputStream os = response.getOutputStream();) {fis = new FileInputStream(file);bis = new BufferedInputStream(fis);int i = bis.read(buffer);while (i != -1) {os.write(buffer, 0, i);i = bis.read(buffer);}} catch (Exception e) {// do something, // probably forward to an Error page}}return null;}
}

login方法,将用户带到登录表单页面

<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE HTML>
<html>
<head>
<title>Login</title>
<style type="text/css">@import url("<c:url value="/css/main.css"/>");</style>
</head>
<body>
<div id="global">
<form:form commandName="login" action="login" method="post"><fieldset><legend>Login</legend><p><label for="userName">User Name: </label><form:input id="userName" path="userName" cssErrorClass="error"/></p><p><label for="password">Password: </label><form:password id="password" path="password" cssErrorClass="error"/></p><p id="buttons"><input id="reset" type="reset" tabindex="4"><input id="submit" type="submit" tabindex="5" value="Login"></p></fieldset>
</form:form>
</div>
</body>
</html>

用户名和密码在login方法中使用硬编码的方式模拟用户登录,成功后跳转到Main.jsp页面,该页面包含一个超链接,点击下载文件。

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE HTML>
<html>
<head>
<title>Download Page</title>
<style type="text/css">@import url("<c:url value="/css/main.css"/>");</style>
</head>
<body>
<div id="global"><h4>Please click the link below.</h4><p><a href="resource_download">Download</a></p>
</div>
</body>
</html>

测试

点击链接

查看下载的文件


防止交叉引用

为了防止他人引用我们网站的资源,可以通过编程的方式,只有当请求的报头referer标题中包含你的域名时才发出资源,当然了这种方式也不能完全阻止。

该示例中,ImageController类中,只有referer标题不为空时,才将图片发送给浏览器

package com.artisan.controller;import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;@Controller
public class ImageController {@RequestMapping("/imageList")public String getImageList(){return "Image"; }@RequestMapping(value = "/image_get/{id}", method = RequestMethod.GET)public void getImage(@PathVariable String id, HttpServletRequest request, HttpServletResponse response,@RequestHeader String referer) {// 判断请求头中的Refererif (referer != null) {String imageDirectory = request.getServletContext().getRealPath("/WEB-INF/image");File file = new File(imageDirectory, id + ".jpg");if (file.exists()) {response.setContentType("image/jpg");byte[] buffer = new byte[1024];FileInputStream fis = null;BufferedInputStream bis = null;// if you're using Java 7, use try-with-resourcestry {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 ex) {// do something here} finally {if (bis != null) {try {bis.close();} catch (IOException e) {}}if (fis != null) {try {fis.close();} catch (IOException e) {}}}}}}
}

前台页面

<!DOCTYPE HTML>
<html>
<head>
<title>Images</title>
</head>
<body>
<img src="data:image_get/1"/>
<img src="data:image_get/2"/>
<img src="data:image_get/3"/>
<img src="data:image_get/4"/>
<img src="data:image_get/5"/>
<img src="data:image_get/6"/>
<img src="data:image_get/7"/>
<img src="data:image_get/8"/>
<img src="data:image_get/9"/>
<img src="data:image_get/10"/>
</body>
</html>

测试:


源码

代码已提交到github

https://github.com/yangshangwei/SpringMvcTutorialArtisan

Spring MVC-10循序渐进之文件下载相关推荐

  1. Spring MVC 文件上传 文件下载

    索引: 目录索引 参看代码 GitHub: pom.xml WebConfig.java index.jsp upload.jsp FileUploadController.java Files_Ut ...

  2. Spring MVC(10):REST 支持 Ajax+Spring MVC 实例

    Spring 对于 REST 的支持 REST REST(Respresentational State Transfer) 是一种面向资源,强调描述应用程序远程调用的开发方式,并不特指某种技术和框架 ...

  3. spring MVC 获取servletContext,实现文件下载功能

    以下是获取servletContext: import javax.servlet.ServletContext;import org.springframework.web.context.Cont ...

  4. zbb20180613 Spring MVC实现大文件下载功能

    Spring MVC实现大文件下载功能 pom.xml <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xs ...

  5. Spring MVC的DispatcherServlet – Java开发人员应该知道的10件事

    如果您使用过Spring MVC,那么您应该知道什么是DispatcherServlet? 它实际上是Spring MVC的心脏,确切地说是MVC设计模式或控制器的C语言. 应该由Spring MVC ...

  6. Spring MVC-03循序渐进之Spring MVC

    概述 前面两篇介绍了模型2架构的优势以及如何构建一个模型2应用.但是Spring MVC框架可以帮助我们快速的开发MVC应用. Spring MVC体系概述 若基于某个框架来开发一个模型2的应用程序, ...

  7. Java EE:第10章初识Spring MVC框架 课后习题

    <Java EE企业级应用开发教程 第2版(Spring+Spring MVC+MyBatis)>黑马程序员编著 中国工信出版集团 人民邮电出版社 目录 1.请简述MyBatis与Spri ...

  8. Http请求中Content-Type讲解以及在Spring MVC注解中produce和consumes配置详解

    转载自https://blog.csdn.net/shinebar/article/details/54408020 引言: 在Http请求中,我们每天都在使用Content-type来指定不同格式的 ...

  9. Http请求中Content-Type讲解以及在Spring MVC中的应用

    引言: 在Http请求中,我们每天都在使用Content-type来指定不同格式的请求信息,但是却很少有人去全面了解content-type中允许的值有多少,这里将讲解Content-Type的可用值 ...

  10. [转]Http请求中Content-Type讲解以及在Spring MVC中的应用

    本文转自:http://blog.csdn.net/blueheart20/article/details/45174399 引言: 在Http请求中,我们每天都在使用Content-type来指定不 ...

最新文章

  1. linux基础知识复习
  2. 删除桌面快捷方式小图标的bat命令
  3. 『数据库』 E-R图(实体联系图)你都不会,你设计什么数据库?
  4. Mysql 的子查询
  5. java包装项目_项目包装组织
  6. matlab 矩阵引用,MATLAB矩阵生成、引用
  7. 居家隔离14+7天的第三天 2021年8月1日15:49:09
  8. JS内存泄漏实例解析
  9. HDU1013 POJ1519 Digital Roots(解法三)【废除!!!】
  10. 【仿某公司前台】 asp安全查询系统
  11. Java 多线程(二)启动线程的三种方式和源码分析
  12. 2022年高压电工考试模拟100题及在线模拟考试
  13. iOS14捷径------番茄钟2.0
  14. 闲话英特尔发展史中的尴尬瞬间(1)-名不副实的MMX
  15. 与虎谋皮,饮鸩止渴,却有什么办法呢?
  16. 交互式shell脚本实操
  17. Model-Agnostic Meta-Learning for Fast Adaptation of Deep Networks(MAML)研读笔记
  18. 单台服务器做redis集群
  19. 一小部分机器学习算法小结: 优化算法、逻辑回归、支持向量机、决策树、集成算法、Word2Vec等...
  20. 实战大项目:模拟登录丁香园,并抓取论坛页面所有的人员基本信息与回复帖子内容----登录第一步

热门文章

  1. div超出不换行_DIV元素不换行
  2. sql 字符串函数(一)
  3. 稳定匹配算法python实现
  4. Tensorflow实现MNIST数据自编码(3)
  5. 171. Excel Sheet Column Number
  6. 文巾解题383. 赎金信
  7. 机器学习笔记:logistic regression
  8. 滴滴算法大赛算法解决过程 - 拟合算法
  9. 使用 Python 进行稳定可靠的文件操作
  10. Python/Anaconda-python2.x代码转为python3.x代码