大家好,欢迎来到乐字节小乐的Java技术分享园地
回顾上篇内容:
乐字节:web请求和常用对象(续)|乐字节zhuanlan.zhihu.com一、Session
1. 介绍
HttpSession对象是 javax.servlet.http.HttpSession 的实例,该接口并不像 HttpServletRequest 或 HttpServletResponse 还存在一个父接口,该接口只是一个纯粹的接口。这因为 session 本身就属于 HTTP 协议的范畴。
对于服务器而言,每一个连接到它的客户端都是一个 session,servlet 容器使用此接口创建 HTTP 客户端和 HTTP 服务器之间的会话。会话将保留指定的时间段,跨多个连接或来自用户的页面请求。一个会话通常对应于一个用户,该用户可能多次访问一个站点。可以通过此接口查看和操作有关某个会话的信息,比如会话标识符、创建时间和最后一次访问时间。在整个 session 中,最重要的就是属性的操作。
![69a6a4d5687ff42310759fa69a896c6a.png](https://i-blog.csdnimg.cn/blog_migrate/9b00bf44813756a3cd1b0dd53462d9ea.png)
session 无论客户端还是服务器端都可以感知到,若重新打开一个新的浏览器,则无法取得之前设置的 session,因为每一个 session 只保存在当前的浏览器当中,并在相关的页面取得。
2. Session 的作用
Session 的作用就是为了标识一次会话,或者说确认一个用户;并且在一次会话(一个用户的多次请求)期间共享数据。我们可以通过 req.getSession()方法,来获取当前会话的 session 对象。
![676e54d11505642d83e9bcdfcdaf8e8a.png](https://i-blog.csdnimg.cn/blog_migrate/407189e4970f32a8c534743978428d31.png)
3. 标识会话 JSESSIONID
Session 既然是为了标识一次会话,那么此次会话就应该有一个唯一的标志,这个标志就是 sessionId。
每当一次请求到达服务器,如果开启了会话(访问了 session),服务器第一步会查看是否从客户端回传一个名为 JSESSION 的 cookie,如果没有则认为这是一次新的会话,会创建 一个新的 session 对象,并用唯一的 sessionId 为此次会话做一个标志。如果有 JESSIONID 这 个cookie回传,服务器则会根据JSESSIONID这个值去查看是否含有id为JSESSION值的session 对象,如果没有则认为是一个新的会话,重新创建一个新的 session 对象,并标志此次会话; 如果找到了相应的 session 对象,则认为是之前标志过的一次会话,返回该 session 对象,数据达到共享。
这里提到一个叫做 JSESSION 的 cookie,这是一个比较特殊的 cookie,当用户请求服务器时,如果访问了 session,则服务器会创建一个名为 JSESSION,值为获取到的 session(无论是获取到的还是新创建的)的 sessionId 的 cookie 对象,并添加到 response 对象中,响应给客户端,有效时间为关闭浏览器。
所以 Session 的底层依赖 Cookie 来实现。
4. 作为域对象
Session 用来表示一次会话,在一次会话中数据是可以共享的,这时 session 作为域对象存在,可以通过 setAttribute(name,value);方法向域对象中添加数据,通过getAttribute(name) 从域对象中获取数据,通过 removeAttribute(name)从域对象中移除数据。
![3e236a22eeebdb2e2668cfe206ba0526.png](https://i-blog.csdnimg.cn/blog_migrate/0e879a17798a537119cdd888a14e0b82.png)
数据存储在 session 域对象中,当 session 对象不存在了,或者是两个不同的 session 对 象时,数据也就不能共享了。这就不得不谈到 session 的生命周期。
5. Session 的销毁
1) 默认时间到期
当客户端第一次请求 servlet 并且操作 session 时,session 对象生成,Tomcat 中 session 默认的存活时间为 30min,即你不操作界面的时间,一旦有操作,session 会重新计时。那么 session的默认事件可以改么?答案是肯定的。可以在Tomcat中的web.xml文件中进行修改。 如下图:
![a23da8c12c79772d00c281c63edc49c3.png](https://i-blog.csdnimg.cn/blog_migrate/7ff660c7885ab90d5b9097c0ac0ccce7.png)
2) 自己设定到期时间
当然除了以上的修改方式外,我们也可以在程序中自己设定 session 的生命周期,通过session.setMaxInactiveInterval(int);来设定 session 的最大不活动时间,单位为秒。
![23fb915098f1e8d461be455c80db1651.png](https://i-blog.csdnimg.cn/blog_migrate/d8c6cd8f3c34d86f91e03818f7e05d01.png)
当然我们也可以通过 getMaxInactiveInterval();方法来查看当前 Session 对象的最大不活动时间。
3) 立刻失效
或者我们也可以通过 session.invalidate();方法让 session 立刻失效
![37187cf2938d442f0c7dd628e2890b8a.png](https://i-blog.csdnimg.cn/blog_migrate/d810b8fd25307600b3907eae4584ef8a.png)
4) 关闭浏览器
从前面的 JESSION 可知道,session 的底层依赖 cookie 实现,并且该 cookie 的有效时间为关闭浏览器,从而 session 在浏览器关闭时也相当于失效了(因为没有 JSESSION 再与之对应)。
5) 关闭服务器
当非正常关闭服务器时,session 销毁;当正常关闭服务器时,Session 将被序列化到磁盘上,在工作空间 work 目录下的 SESSION.ser 文件中,下次启动服务时,自动加载到内存。
Session 失效则意味着此次会话结束,数据共享结束。
二、ServletContext 对象
每一个 web 应用都有且仅有一个ServletContext 对象,又称 Application 对象,从名称中可知,该对象是与应用程序相关的。在 WEB 容器启动的时候,会为每一个 WEB 应用程序创 建一个对应的 ServletContext 对象。
该对象有两大作用,第一、作为域对象用来共享数据,此时数据在整个应用程序中共享; 第二、该对象中保存了当前应用程序相关信息。例如可以通过 getServerInfo();方法获取当前 服 务 器 信 息 , getResourceAsStream(String path); 方法以流的形式获取某个资源,getRealPath(String path);获取资源的真实路径等。
1. ServletContext对象的获取
获取 ServletContext 对象的途径有很多例如通过 request 对象可以
![aa2aa43eb45467bce99a5c673148ef83.png](https://i-blog.csdnimg.cn/blog_migrate/c06fa42cdd50e6d6b247e5cd19e45b96.png)
通过 session 可以
![aa2aa43eb45467bce99a5c673148ef83.png](https://i-blog.csdnimg.cn/blog_migrate/c06fa42cdd50e6d6b247e5cd19e45b96.png)
通过servletConfig可以,在 Servlet 标准中提供了获取 ServletConfig 的方法
![ed48c5c2105a1da391c286e5c780d6a5.png](https://i-blog.csdnimg.cn/blog_migrate/215ae839db704bc30fc6111bdca6123f.png)
本身Servlet标准后期的实现类中也提供了直接获取 ServletContext 的方法
![ac5652f1f981eaa11d8c3b3d5d4651e6.png](https://i-blog.csdnimg.cn/blog_migrate/124193861e39480d000ddaa4a8d7371f.png)
2. 作为域对象
ServletContext 也可当做域对象来使用,通过向 ServletContext 中存取数据,可以使得整个应用程序共享某些数据。当然不建议存放过多数据,因为 ServletContext 中的数据一旦存储进去没有手动移除将会一直保存。
![c6d065842edd03de5b59782dbaa4381d.png](https://i-blog.csdnimg.cn/blog_migrate/c588dedd1f38fd4a18467761c1cb0205.png)
到此了解了ServletContext的常用方法,后期使用到更多方法,查阅API即可。
三、文件上传下载
在上网的时候我们常常遇到文件上传的情况,例如上传头像、上传资料等;当然除了上传,遇见下载的情况就更多了,接下来看看我们 servlet 中怎么实现文件的上传和下载。下面我们使用 commons-fileupload 来做文件上传。
1. 文件上传
文件上传涉及到前台页面的编写和后台服务器端代码的编写,前台发送文件,后台接收 并保存文件,这才是一个完整的文件上传。
1) 前台页面
在做文件上传的时候,会有一个上传文件的界面,首先我们需要一个表单,并且表单的 请求方式为 POST;其次我们的 form 表单的 enctype 必须设为”multipart/form-data”即 enctype="multipart/form-data"意思是设置表单的 MIME 编码。默认情况下这个编码格式 是 ”application/x-www-form-urlencoded”, 不能用于文件上传;只有使用了multipart/form-data 才能完整地传递文件数据。
![07e514a21eb99badfd5e8d197bedf976.png](https://i-blog.csdnimg.cn/blog_migrate/dacaa46cb1429a65d6e4a2d5a98f6659.png)
6) 后台 commons-fileupload 的使用
首先需要导入第三方jar包,http://commons.apache.org/ 下 载 commons-io 和 commons-fileupload 两个jar的资源。解压并导入到项目中。 commons-fileupload.jar 是文件上传的核心包 commons-io.jar 是 filefupload 的依赖包,同时又是一个工具包。
![2c354dbc4065b65261f1b50736273269.png](https://i-blog.csdnimg.cn/blog_migrate/6002e840bce0a10d95a761de503f273e.png)
介绍一下使用到的几个核心类
DiskFileItemFactory – 设置磁盘空间,保存临时文件。只是一个工具类 ServletFileUpload – 文件上传的核心类,此类接收 request,并解析 ServletFileUpload.parseRequest(request); – List 解析 request
1、 创建一个 DiskFileItemFactory 工厂类,并制定临时文件和大小
2、 创建 ServletFileUpload 核心类,接收临时文件,做请求的转换
3、 通过 ServletFileUpload 类转换原始请求,得到 FileItem 集合
4、 遍历集合中的各个元素并处理
5、 判断每个元素是否是普通表单项,如果是则按照普通表单项处理
6、 如果不是普通表单项,则是文件,通过处理的方式进行处理(上传)
public class UploadSource extends HttpServlet {
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 设定编码,可以获取中文文件名
req.setCharacterEncoding("UTF-8");
// 获取tomcat下的upload目录的路径
String path = getServletContext().getRealPath("/upload");
// 临时文件目录
String tempPath = getServletContext().getRealPath("/temp");
// 检查我们是否有文件上传请求
// boolean isMultipart = ServletFileUpload.isMultipartContent(req);
// 1、声明DiskFileItemFactory工厂类,用于在指定磁盘上设置一个临时目录
DiskFileItemFactory disk = new DiskFileItemFactory(1024 * 10, new File(tempPath));
// 2、声明ServletFileUpload,接收上面的临时文件。也可以默认值
ServletFileUpload up = new ServletFileUpload(disk);
// 3、解析request
try {
List<FileItem> list = up.parseRequest(req);
if (list.size() > 0) {
for (FileItem file : list)
// 判断是否是普通的表单项
if (file.isFormField()) {
String fieldName = file.getFieldName();
// 中文乱码,此时还需要指定获取数据的编码方式
// String value = file.getString();
String value = file.getString("UTF-8");
System.out.println(fieldName + "=" + value);
} else { // 说明是一个文件
// 获取文件本身的名称
String fileName = file.getName();
System.out.println(file.getFieldName());
// 处理文件名称
fileName = fileName.substring(fileName.lastIndexOf("") + 1);
System.out.println("old Name : " + fileName);
// 修改名称
String extName = fileName.substring(fileName.lastIndexOf("."));
String newName = UUID.randomUUID().toString().replace("-", "") + extName;
// 保存新的名称,并写出到新文件中
file.write(new File(path + "/" + newName));
System.out.println("文件名是:" + fileName);
System.out.println("文件大小是:" + file.getSize());
file.delete();
}
}
} catch (FileUploadException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}
7. 文件下载
文件下载即将服务器上的资源下载(拷贝)到本地,我们可以通过两种方式下载。第一种是通过超链接本身的特性来下载;第二种是通过手动写出来下载。
1) 超链接下载
当我们在 HTML 或 JSP 页面中使用标签时,原意是希望能够进行跳转,但当超链接遇到浏览器不识别的动态网页时则会自动下载。例如超链接下载但当遇见浏览器能够直接显示的资源,浏览器就会默认显示出来,比如 txt,png,jpg 等。当然我们也可以通过 download 属性规定浏览器进行下载。但有些浏览器并不支持。
默认下载
![f8b2e1926087e88561b40e8b21e623cb.png](https://i-blog.csdnimg.cn/blog_migrate/85a2e0018895774efc8ac524c2279869.png)
指定 download 属性下载
![fd3bf980578dc4af91ee856b242453b7.png](https://i-blog.csdnimg.cn/blog_migrate/d9788f3a7bb0a233feddb8ae879f7607.png)
这里,download 也可以不写任何信息,会自动使用默认文件名。这样当用户打开浏览 器点击链接的时候就会直接下载文件。
7) 后台实现下载
Step1:需要通过 HttpServletResponse.setContentType 方法设置 Content-type 头字段的值, 为浏览器无法使用某种方式或激活某个程序来处理的 MIME 类型,例 如 ”application/octet-stream” 或 ”application/x-msdownload” 等
Step2:需要通过 HttpServletResponse.setHeader 方法设置 Content-Disposition 头的值 为”attachment;filename=文件名”
Step3: 读取下载文件,调用HttpServletResponse.getOutputStream方法返回的OutputStream对象来向客户端写入附件内容。
public class DownloadServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
// 获取文件下载路径
String path = getServletContext().getRealPath("/") + "download/";
String fileName = req.getParameter("filename");
File file = new File(path + fileName);
if (file.exists()) {
// 设置相应类型 application/octet-stream
resp.setContentType("application/x-msdownload");
// 设置头信息
resp.setHeader("Content-Disposition", "attachment;filename=" + fileName);
InputStream is = new FileInputStream(file);
ServletOutputStream os = resp.getOutputStream();
byte[] car = new byte[1024];
int len = 0;
while ((len = is.read(car)) != -1) {
os.write(car, 0, len);
}
// 关闭流、释放资源
os.close();
is.close();
} else {
System.out.println("文件不存在,下载失败!");
}
}
}
感谢阅读乐字节技术文章,请继续关注乐字节或添加君羊:⑧⑥⑧⑤零④零⑨⑨,更多Java技术文章奉上,下次小乐将为大家带来过滤器及监听器的详细讲解!