Servlet
概述
随着Web应用业务需求的增多,动态Web资源的开发变得越来越重要。目前,很多公司都提供了开发动态Web资源的相关技术,其中,比较常见的有ASP、PHP、JSP和Servlet等。基于Java的动态Web资源开发,Sun公司提供了Servlet和JSP两种技术。
Servlet: server applet: 运行在服务器端的小程序。Java提供了Servlet接口,此接口的实现类可以运行在服务器上。Servlet 主要用于处理客户端传来的 HTTP 请求,并返回一个响应。
Servlet由Servlet容器提供,所谓的Servlet容器是指提供了Servlet 功能的服务器,Servlet容器将Servlet动态地加载到服务器上。与HTTP 协议相关的Servlet使用HTTP请求和HTTP响应与客户端进行交互。因此,Servlet容器支持所有HTTP协议的请求和响应。Servlet应用程序的体系结构如图所示。
Servlet技术特点:
- 方便:Servlet提供了大量的实用工具,如处理很难完成的HTML表单数据、读取和设置HTTP头,以及处理Cookie和跟踪会话等。
- 跨平台:Servlet用Java类编写,可以在不同操作系统平台和不同应用服务器平台下运行。
- 灵活性和可扩展性:采用Servlet开发的Web应用程序,由于Java类的继承性及构造函数等特点,使得应用灵活,可随意扩展。
入门
-
创建JavaEE项目
-
定义一个类,实现Servlet接口。
-
在web.xml中配置:
<servlet> <servlet-name>demo1</servlet-name> <servlet-class>ccom.example.tomcat.ServletDemo1</servlet-class> </serblet> <servlet-mapping> <servlet-name>demo1</servlet-name> <url-pattern>/demo1</url-pattern> </servlet-mapping>
执行原理:
- 当服务器接受到客户端浏览器的请求后,会解析请求URL路径,获取访问的Servlet的资源路径
- 查找web.xml文件,是否有对应的<url-pattern>标签体内容。
- 如果有,则在找到对应的<servlet-class>全类名
- tomcat会将字节码文件加载进内存,并且创建其对象
- 调用其方法
Servlet常用方法
-
被创建时:执行init方法,只执行一次。
默认情况下,第一次被访问时,创建Servlet。
可以修改Servlet的创建时机,在<Servlet>里修改<load-on-startup>的值。<servlet> <servlet-name>demo2</servlet-name> <servlet-class>com.example.tomcat.ServletDemo2</servlet-class> <!-- 指定Servlet的创建时机 1.第一次被访问时创建 <load-on-startup>的值为负数 2.在服务器启动时创建 <load-on-startup>0~9999</load-on-startup>数字小的先 --> <load-on-startup>5</load-on-startup> </servlet>
Servlet的init方法只执行一次,说明一个Servlet在内存中只存在一个对象,Servlet是单例的。
- 多个用户同时访问时,可能存在线程安全问题。
- 解决:尽量不要再Servlet中定义成员变量。即使定义了成员变量,也不要对其修改。
-
提供服务:执行service方法。
每次访问Servlet时,都执行一次service。 -
被销毁:destroy方法
在服务器正常关闭时,Servlet被销毁,才会执行destroy方法。
Servlet3.0:支持注解配置,不需要写麻烦的web.xml了
-
创建JavaEE项目,Servlet版本选3.0以上,可以不创建web.xml
-
定义一个类,实现Servlet接口。
-
在类上使用@WebServlet注解,如下配置
@WebServlet("资源路径")
urlpartten:访问该Servlet的虚拟路径
- 一个Servlet可以定义多个访问路径:@WebServlet({"/demo1","/d1","/dm1"})
- 路径定义规则:
- /xxx
- xxx/yyy 多层目录
- *.do 扩展名匹配
WebServlet的源码
public @interface WebServlet { String name() default ""; //相当于<Servlet-name> String[] value() default {};//代表urlPattern String[] urlPatterns() default {};//同上 int loadOnStartup() default -1;//相当于<load-on-sta WebInitParam[] initParams() default {}; boolean asyncSupported() default false; String smallIcon() default ""; String largeIcon() default ""; String description() default ""; String displayName() default ""; }
Servlet体系结构
接口Servlet-->抽象类GenericServlet-->抽象类HttpServlet
Servlet接口的实现类要重写5个方法,其实一般只需要使用service方法,如果重写5个方法就很麻烦。Java提供了两个抽象类方便我们的使用。
- GenericServlet:将Servlet接口中其他的方法做了默认空实现,只将service()方法作为抽象。此类没有实现http请求处理。
- HttpServlet:http协议的一种封装,简化操作。定义类继承HttpServlet并重写doGet/doPost方法,就能轻松的处理客户端的数据。也可以像基本的Servlet类一样重写service方法,不区分get和post方法,采用相同的程序来处理。
Servlet使用细节
生命周期
出生:请求第一次到达Servlet时,对象就创建出来,并且初始化成功。只出生一次,就放到内存中。
活着:服务器提供服务的整个过程中,该对象一直存在,每次只是执行service方法。
死亡:当服务停止时,或者服务器宕机时,对象消亡。
通过分析Servlet的生命周期我们发现,它的实例化和初始化只会在请求第一次到达Servlet时执行,而销毁只会在Tomcat服务器停止时执行,由此我们得出一个结论,Servlet对象只会创建一次,销毁一次。所以,Servlet对象只有一个实例。如果一个对象实例在应用中是唯一的存在,那么我们就说它是单实例的,即运用了单例模式。
线程安全
因为Servlet是单例,单例对象的类成员只会随类实例化时初始化一次,之后的操作都是改变,而不会重新初始化。所以可能存在线程安全性问题。所以在Servlet中定义类成员要慎重。如果类成员是共用的,并且只会在初始化时赋值,其余时间都是获取的话,那么是没问题。如果类成员并非共用,或者每次使用都有可能对其赋值,那么就要考虑线程安全问题了,把它定义到doGet或者doPost方法里面去就可以了。
不用全局变量即可。
Servlet依赖
单独的Servlet还不能提供服务,还需要依赖几个变量:ServletRequest用来封装客户端请求,ServletResponse用来向客户端发送响应,ServletConfig保存Servlet的配置信息,ServletContext保存整个web项目的信息。
ServletConfig
每个Servlet都一个自己的ServletConfig,保存了此Servlet的配置信息。它在初始化阶段读取了web.xml中为Servlet准备的初始化配置,并把配置信息传递给Servlet,所以生命周期与Servlet相同。
ServletContext
概述
ServletContext对象,它是应用上下文对象。每一个应用有且只有一个ServletContext对象。它可以实现让应用中所有Servlet间的数据共享。 通常网站下方的 copyright 和备案号都是通过ServletContext对象保存的。
生命周期
出生: 应用一加载,该对象就被创建出来了。一个应用只有一个实例对象。(Servlet和ServletContext都是单例的)
活着:只要应用一直提供服务,该对象就一直存在。
死亡:应用被卸载(或者服务器挂了),该对象消亡。
域对象
域对象的概念,它指的是对象有作用域。域对象可以实现数据共享。不同作用范围的域对象,共享数据的能力不一样。
在Servlet规范中,一共有4个域对象。ServletContext是web应用中最大的作用域,叫application域。每个应用只有一个application域。它可以实现整个应用间的数据共享功能。
Session会话域
request请求域
pageContext页面域:jsp特有
ServletContext的使用
ServletContext 是应用上下文对象。每一个应用中只有一个 ServletContext 对象。
作用:可以获得应用的全局初始化参数和达到 Servlet 之间的数据共享。
生命周期:应用一加载则创建,应用被停止则销毁。
ServletContext配置
ServletContext既然被称之为应用上下文对象,所以它的配置是针对整个应用的配置,而非某个特定Servlet的配置。它的配置被称为应用的初始化参数配置。
配置的方式,需要在<web-app>
标签中使用<context-param>
来配置初始化参数。具体代码如下:
<!--配置应用初始化参数-->
<context-param>
<!--用于获取初始化参数的key-->
<param-name>servletContextInfo</param-name>
<!--初始化参数的值-->
<param-value>This is application scope</param-value>
</context-param>
<!--每个应用初始化参数都需要用到context-param标签-->
<context-param>
<param-name>globalEncoding</param-name>
<param-value>UTF-8</param-value>
</context-param>
常用方法
public class ServletContextDemo extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
/**
获取ServletContext对象
1. 通过request对象获取:request.getServletContext();
2. 通过Servlet对象获取:this.getServletContext();
*/
ServletContext context = getServletContext();
//获取全局配置的globalEncoding
String value = context.getInitParameter("globalEncoding");
System.out.println(value);
//获取应用的访问虚拟目录
String contextPath = context.getContextPath();
System.out.println(contextPath);
//文件有两个路径,一个是idea的工作空间路径,一个是部署在服务器上的路径
//根据虚拟目录获取应用部署的磁盘绝对路径
//获取webapp/b.txt文件的绝对路径
String b = context.getRealPath("/b.txt");
System.out.println(b);
//获取webapp/WEB-INF/c.txt文件的绝对路径
String c = context.getRealPath("/WEB-INF/c.txt");
System.out.println(c);
//获取java类目录下的a.txt文件的绝对路径
String a = context.getRealPath("/WEB-INF/classes/a.txt");
System.out.println(a);
//向域对象中存储数据
context.setAttribute("username","zhangsan");
//移除域对象中username的数据
//context.removeAttribute("username");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
ServletRequest
Servlet最主要的作用就是处理客户端请求,并向客户端做出响应。为此,针对Servlet的每次请求,Web服务器在调用service()方法之前,都会创建两个对象,分别是HttpServletRequest和 HttpServletResponse。其中,HttpServletRequest 用于封装 HTTP 请求消息,简称 request对象。HttpServletResponse 用于封装 HTTP 响应消息,简称 response 对象。
-
request对象和response对象的原理
- request和response对象是由服务器创建的。我们来使用它们
- request对象是来获取请求消息,response对象是来设置响应消息
-
request对象继承体系结构
ServletRequest(interface) —>HttpServletRequest(interface)—>org.apache.catalina.connector.RequestFacade(class from tomcat) -
request功能:
-
获取请求消息数据
-
获取请求行数据
GET /day14/demo1?name=zhangsan HTTP/1.1
方法:-
获取请求方式 :String getMethod() GET
-
获取虚拟目录:String getContextPath() /day14。ServletContext也有这个方法。
-
获取Servlet路径:String getServletPath() /demo1
-
获取get方式请求参数:String getQueryString() name=zhangsan
-
获取请求URI:String getRequestURI(): /day14/demo1
StringBuffer getRequestURL(): http://localhost/day14/demo1
URL:统一资源定位符
URI:统一资源标识符 -
获取协议及版本:String getProtocol() HTTP/1.1
-
获取客户机的IP地址:String getRemoteAddr()
-
获取ServletContext:ServletContext getServletContext()
-
@WebServlet("/RequestDemo1") public class RequesDemo1 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //1. 获取请求方式 String getMethod() System.out.println(req.getMethod()); // 2. 获取虚拟目录: String getContextPath() System.out.println(req.getContextPath()); //3. 获取Servlet路径: String getServletPath() System.out.println(req.getServletPath()); //4. 获取get方式请求参数 String getQueryString() System.out.println(req.getQueryString()); //5. 获取请求URI:/day14/demo1 // String getRequestURI(): /day14/demo1 // StringBuffer getRequestURL(): http://localhost/day14/demo1 System.out.println(req.getRequestURI()); System.out.println(req.getRequestURL()); //6. 获取协议及版本: String getProtocol() System.out.println(req.getProtocol()); //7. 获取客户机的IP地址: String getRemoteAddr() System.out.println(req.getRemoteAddr()); } }
-
-
获取请求头数据
String getHeader(String name):通过请求头的名称获取请求头的值
常用的头:user-agent; referer@WebServlet("/rd3") public class RequestDemo3 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String ua = req.getHeader("user-agent"); if(ua.contains("Chrome")){ System.out.println("谷歌来了.."); }else if(ua.contains("Firefox")){ System.out.println("火狐来了.."); } } }
Enumeration<String> getHeaderNames():获取所有的请求头名称
@WebServlet("/rd2") public class RequestDemo2 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //1.获取所有请求头名称 Enumeration<String> headerNames = req.getHeaderNames(); //2.遍历 while(headerNames.hasMoreElements()){//Enumeration接口见jdk文档 //根据名称获取请求头的值 String name = headerNames.nextElement(); String value = req.getHeader(name); System.out.println(name+"------"+value); } } }
-
获取请求参数
-
如果是post请求方式,可以使用流对象获取请求体。步骤:
-
获取流对象
BufferedReader getReader() 获取字符输入流,只能获取文本流行
ServletInputStream getInputStream() 获取字节输入流,可以操作所有类型数据,用于文件上传 -
再从流对象中拿数据
@WebServlet("/rd5") public class RequestDemo5 extends HttpServlet { @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //获取请求体 //1.获取字符流 BufferedReader reader = req.getReader(); //2.读取数据 String line = null; while ((line = reader.readLine())!=null){ System.out.println(line); } } }
-
-
通用方式:不论post还是get都能用
String getParameter(String name): 根据参数名称获取参数值
String[] getParametersValues(String name): 根据参数名称获取参数值的数组,一般用于复选框
Enumeration<String> getParameterNames(): 获取所有请求的参数名称
Map<String,String[]> getParameterMap():获取所有参数名和值的map集合
上面的name和value其实就是html标签体的name属性值和value属性值
中文乱码问题:
get方式:tomcat8已经解决中文乱码方式
post方式:会乱码,在获取参数前加一个代码:req.setCharacterEncoding("utf-8")
-
-
-
请求转发
请求转发:一个Servlet接收请求然后发送给另一个Servlet处理
当一个Web资源收到客户端的请求后,如果希望服务器通知另外一个资源去处理请求,可以通过RequestDispatcher接口的实例对象来实现。在ServletRequest接口中定义了一个获取RequestDispatcher对象的方法。
获取了RequestDispatcher对象后,可以通过两个相关的方法通知其他的Servlet处理请求。
请求转发步骤
-
通过request对象获取请求转发器对象:RequestDispatcher getRequestDispatcher(String path);
-
使用RequestDispatcher对象来转发:forward(ServeletRequest req, ServletResponse resp);
@WebServlet("/rd7") public class RequestDemo7 extends HttpServlet { @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("demo7被访问了。。。"); //获取转发对象并转发 // RequestDispatcher requestDispatcher = req.getRequestDispatcher("/rd8"); // requestDispatcher.forward(req,resp); req.getRequestDispatcher("/rd8").forward(req,resp); } }
特点
- 浏览器地址栏路径不变
- 只能转发到服务器的内部资源中
- 转发是一次请求
请求包含
指的是使用include()方法将Servlet请求转发给其他Web资源进行处理,与请求转发不同的是,在请求包含返回的响应消息中,既包含了当前Servlet的响应消息,也包含了其他Web资源所作出的响应消息。
/**
* 请求包含
* 它是把两个Servlet的响应内容合并输出。
* 注意:这种包含是动态包含。
* 动态包含的特点:各编译各的,只是最后合并输出。
*/
public class RequestDemo8 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.getWriter().write("I am request demo8 ");
//1.拿到请求调度对象
RequestDispatcher rd = request.getRequestDispatcher("/RequestDemo9");
//2.实现包含的操作
rd.include(request, response);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
/**
* 被包含者
*/
public class RequestDemo9 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.getWriter().write("include request demo 9 ");
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
ServletResponse
功能:设置响应消息
-
设置响应行:
设置状态码:setStatus(int sc) 。正常情况下,Web 服务器会默认产生一个状态码为200的状态行。
错误信息状态码:setError(int sc)。用于发送表示错误信息的状态码,例如,404状态码表示找不到客户端请求的资源。
sendError(int code, String message)。此重载方法除了发送状态码外,还可以增加一条用于提示说明的文本信息,该文本信息将出现在发送给客户端的正文内容中。 -
设置响应头:
addHeader()、setHeader()、addInt Header()、setIntHeader()方法都是用于设置各种头字段的,而setContentType()、setLocale()和setCharacterEncoding()方法用于设置字符编码,这些设置字符编码的方法可以有效解决乱码问题。resp.setContentType("text/html"); resp.getWriter().print("<h1>hello</h1>");//会解析标签 resp.setContentType("text/plain"); resp.getWriter().print("<h1>hello</h1>");//纯文本,不会解析标签 //MIME类型:text/plain 纯文本、text/html HTML文档、text/xml //application/x-msdownload 需要下载的资源 //image/jpeg image/gif ... 图片资源
-
设置响应体
由于在HTTP响应消息中,大量的数据都是通过响应消息体传递的,因此,ServletResponse遵循以IO流传递大量数据的设计理念。在发送响应消息体时,定义了两个与输出流相关的方法- 第一步获取输出流
字符输出流:PrintWriter getWriter()
字节输出流:ServletOutputStream getOutputStream() - 第二步使用输出流,将数据输出到客户端浏览器
注意:response获得的字符流和字节流只能二选一,不可同时使用,否则会发生IllegalStateException异常。response获得的流不用关闭,服务器会自己关闭。
可以将想要向客户端输出的内容放在HTML标签中,浏览器会自动解析。
- 第一步获取输出流
案例一:完成重定向
代码实现
- 设置状态码302:resp.setStatus(302);
- 设置响应头location: resp.setHeader(“location”,”/respDemo2”);
- 两步合一步的写法:resp.sendRedirect(“/respDemo2”);
重定向的特点:redirect
- 地址栏发生变化
- 重定向可以访问其他站点(服务器)的资源
- 重定向是两次请求。不能使用request对象来共享数据
转发的特点:forword
- 转发地址栏路径不变
- 转发只能访问当前服务器下的资源
- 转发是一次请求,可以使用request对象来共享数据
路径写法:
- 绝对路径:通过绝对路径可以确定唯一资源
http://localhost/day15/responseDemo2 以/开头的路径 - 相对路径:通过相对路径不可以确定唯一资源,关键是找到当前资源和目标资源之间的相对位置关系
./index.html 不以/开头,以.开头路径
./代表当前目录,./可以省略 ../:后退一级目录
路径需不需要加虚拟目录
- 给客户端浏览器使用:需要加虚拟目录(项目的访问路径),比如 HTML 页面中的元素 <a> <form action="/day15/servlet1"> 里的路径
建议虚拟目录动态获取:request.getContextPath(),最好不要写/day15/这种,防止以后改了虚拟目录 - 给服务器使用:不需要加虚拟目录,比如转发路径
案例二:服务器向客户端输出字符
@WebServlet("/respDemo1")
public class RespDemo1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");
PrintWriter pw = resp.getWriter();
pw.println("<h1>hello world</h1>");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
中文乱码问题:
- 看一下浏览器使用的字符集是什么,比如是GBK
- 字符输出流的默认编码是ISO-8859-1,如果和浏览器的字符集不一样就需要更改
- 在获取流的代码前加一句:resp.setCharSetEncoding(“GBK”);
- 但上面的方法不保险,不同的电脑有不一样的编码,可以告诉浏览器按照服务器使用的字符集来解析:resp.setContentType("text/html;charset=utf-8");
这样就万无一失了。
案例三:网页定时刷新并跳转
在 HTTP 协议中,定义了一个Refresh头字段,它可以通知浏览器在指定的时间内自动刷新并跳转到其他页面。
@WebServlet("/refreshDemo")
public class RefreshDemo extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//2秒后刷新并跳转百度
resp.setHeader("refresh","2;https://www.baidu.com");
}
@WebServlet("/refreshDemo")
public class RefreshDemo extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//每3秒刷新一次本页面,常见于购物网站显示库存
resp.setHeader("refresh","3");
}
案例四:生成验证码
public class ResponseDemo3 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
int width = 200;
int height = 35;
/**
* 实现步骤:
* 1.创建图像内存对象
* 2.拿到画笔
* 3.设置颜色,画矩形边框
* 4.设置颜色,填充矩形
* 5.设置颜色,画干扰线
* 6.设置颜色,画验证码
* 7.把内存图像输出到浏览器上
*/
//创建内存图像
BufferedImage image = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);//参数:宽度,高度 (指的都是像素),使用的格式(RGB)
Graphics g = image.getGraphics();//画笔就一根
//设置颜色
g.setColor(Color.BLUE);
//画边框
g.drawRect(0, 0, width, height);
//设置颜色
g.setColor(Color.GRAY);
//填充矩形
g.fillRect(1, 1, width-2, height-2);
//设置颜色
g.setColor(Color.WHITE);
//拿随机数对象
Random r = new Random();
//画干扰线 10条
for(int i=0;i<10;i++){
g.drawLine(r.nextInt(width), r.nextInt(height),r.nextInt(width), r.nextInt(height));
}
//设置颜色
g.setColor(Color.RED);
//改变字体大小
Font font = new Font("宋体", Font.BOLD,30);//参数:1字体名称。2.字体样式 3.字体大小
g.setFont(font);//设置字体
//画验证码 4个
int x = 35;//第一个数的横坐标是35像素
for(int i=0;i<4;i++){
//r.nextInt(10)+""这种写法效率是十分低的
g.drawString(String.valueOf(r.nextInt(10)), x, 25);
x+=35;
}
//输出到浏览器上
//参数: 1.内存对象。2.输出的图片格式。3.使用的输出流
ImageIO.write(image, "jpg", response.getOutputStream());
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
案例五:设置缓存时间
/**
* 设置缓存时间
* 使用缓存的一般都是静态资源
* 动态资源一般不能缓存。
* 我们现在目前只掌握了Servlet,所以用Servlet做演示
* @author 黑马程序员
* @Company http://www.itheima.com
*
*/
public class ResponseDemo4 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String str = "设置缓存时间";
/*
* 设置缓存时间,其实就是设置响应消息头:Expires 但是值是一个毫秒数。
* 使用的是
* response.setDateHeader();
*
* 缓存1小时,是在当前时间的毫秒数上加上1小时之后的毫秒值
*/
response.setDateHeader("Expires",System.currentTimeMillis()+1*60*60*1000);
response.setContentType("text/html;charset=UTF-8");
response.getOutputStream().write(str.getBytes());
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
案例六:文件下载
public class ResponseDemo8 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
/*
* 文件下载的思路:
* 1.获取文件路径
* 2.把文件读到字节输入流中
* 3.告知浏览器,以下载的方式打开(告知浏览器下载文件的MIME类型)
* 4.使用响应对象的字节输出流输出到浏览器上
*/
//1.获取文件路径(绝对路径)
ServletContext context = this.getServletContext();
String filePath = context.getRealPath("/uploads/6.jpg");//通过文件的虚拟路径,获取文件的绝对路径
//2.通过文件路径构建一个字节输入流
InputStream in = new FileInputStream(filePath);
//3.设置响应消息头
response.setHeader("Content-Type", "application/octet-stream");//注意下载的时候,设置响应正文的MIME类型,用application/octet-stream
response.setHeader("Content-Disposition", "attachment;filename=1.jpg");//告知浏览器以下载的方式打开
//4.使用响应对象的字节输出流输出
OutputStream out = response.getOutputStream();
int len = 0;
byte[] by = new byte[1024];
while((len = in.read(by)) != -1){
out.write(by, 0, len);
}
in.close();
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
中文乱码问题
html页面设置:<meta charset="UTF-8">
get方式不用设置,tomcat8自动解决,post需要设置。request设置:req.setCharacterEncoding("UTF-8");
response设置:resp.setContentType("text/html;charset=UTF-8");
在jsp页面中也要设置:<%@page ContentType="text/html;charset=UTF-8"
web.xml常用配置
web.xml除了可以配置Servlet,还有下面的常用配置
-
修改web应用默认首页
<welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> <welcome-file>index.jsp</welcome-file> <welcome-file>login.html</welcome-file> <welcome-file>login.htm</welcome-file> <welcome-file>login.jsp</welcome-file> <welcome-file>error.html</welcome-file> </welcome-file-list> <!-- 可以设置多个默认首页,第一个不能访问就找第二个-->
-
Servlet通配符映射
<servlet> <servlet-name>patternServlet</servlet-name> <servlet-class>com.example.servlet1.patternServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>patternServlet</servlet-name> <url-pattern>/pattern/*</url-pattern> </servlet-mapping> <!-- 通过上面的设置,只要在浏览器输入 虚拟目录/pattern/任意字符 就可以访问了。比如1号员工可以输入/pattern/1 来访问。-->
-
设置全局参数
<!-- 所谓的全局参数其实就是 ServletContext 中保存的信息 --> <context-param> <param-name>copyright</param-name> <param-value>2004-2020 sogou.com 京ICP备11001839号-1</param-value> </context-param> <context-param> <param-name>title</param-name> <param-value>搜狗搜索</param-value> </context-param>
-
设置404、500状态码的默认页面
tomcat默认的错误页面不友好,我们需要手动写几个错误页面,然后在web.xml中配置。这样当发生了错误时就会跳转到我们写好的错误页面。
404错误页面<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> </head> <body> 404 NOT FOUND 请检查你的网址 </body> </html>
web.xml配置
<error-page> <error-code>404</error-code> <location>/error/404.html</location> </error-page> <error-page> <error-code>500</error-code> <location>/error/500.html</location> </error-page>
项目正式发布
可以通过 idea 的 export 将项目导出成一个 war 包,然后将此 war 包复制到 tomcat 的 webapp 目录下。然后双击 startup.bat 启动 tomcat 服务器,该项目就会自动部署。