servlet概念

servlet其实就是运行在服务器的一个小程序

如何去理解呢?我们访问服务器的资源包括静态资源和动态资源,其中静态资源是我们放置的模板,CSS、JS等文件,是不变的。而我们访问的动态资源,是根据我们访问的请求路径,路由到指定的类去加载,运行其对应的方法而给出浏览器资源的响应。那么凭什么我们的服务器会帮我们去加载,去运行指定的方法呢?或者说服务器是怎么做到的呢?我们一定是需要遵循服务器某些规则才行的,而规则在 Java 中 就是接口,Servlet就是被服务器(Tomcat)识别的接口,我们需要根据这个接口来定义响应的方法。

快速入门

  1. 创建一个 Java EE 项目,定义一个类,实现 Servlet 接口

    public class ServletDemo1 implements Servlet
  2. 实现接口中的抽象方法

  3. 在web.xml中配置 Servlet

    <!-- 配置Servlet -->
    <servlet>
    <servlet-name>demo1</servlet-name>
    <servlet-class>org.taoguoguo.web.ServletDemo1</servlet-class>
    </servlet> <servlet-mapping>
    <servlet-name>demo1</servlet-name>
    <url-pattern>/demo1</url-pattern>
    </servlet-mapping>

Servlet执行原理:

  1. 当服务器接受客户端浏览器的请求后,会解析请求的URL路径,获取访问Servlet的资源路径
  2. 查找web.xml文件,是否有对应的 <url-pattern> 标签体内容
  3. 如果有,则会找到对应的 <servlet-class> 全类名
  4. tomcat 会将字节码文件加载进内存,并且创建其对象
  5. 调用其方法

Servlet中的生命周期:

  1. 被创建:执行 init 方法,只执行一次

    • Servlet 什么时候被创建?

      1. 默认情况下, 第一次访问时,Servlet被创建

      2. 可以配置Servlet的创建时机,如下:

        <!-- 配置Servlet -->
        <servlet>
        <servlet-name>demo2</servlet-name>
        <servlet-class>org.taoguoguo.web.ServletDemo2</servlet-class>
        <!--指定Servlet的创建时机
        1.第一次被访问时创建,创建
        <load-on-startup>的值为负数,默认为 -1,或者不配置 也是在第一次访问被创建
        2.在服务器启动时,创建
        <load-on-startup>的值为0或正整数 一般设置为 1
        -->
        <load-on-startup>-1</load-on-startup>
        </servlet> <servlet-mapping>
        <servlet-name>demo2</servlet-name>
        <url-pattern>/demo2</url-pattern>
        </servlet-mapping>

        注意:Servlet的 init 方法只执行一次,说明servlet在内存中只存在一个对象,是单例的,所以多个用户线程同时访问时,可能存在线程安全问题。

        解决方案:

        1.尽量不要在Servlet中定义成员变量,因为成员变量时每个线程共享的资源,可能有的线程会修改,而部分线程会去获取,容易出现数据安全问题。尽量在方法内部定义局部变量代替成员变量。因为局部变量是在每个线程方法栈中独享的一份资源,可以保证数据安全性。

        2.如果必须要定义成员变量,不要在方法中去修改成员变量的值,仅用于线程获取,是不会有线程安全问题的。

        3.在servlet解决多线程安全问题时,切记使用同步锁,因为会非常的消耗性能

  2. 提供服务: 执行 service 方法,执行多次

    • 每次访问 Servlet 时,service 方法都会被调用一次
  3. 被销毁:执行 destory 方法,只执行一次

    • servlet 被销毁时执行,只有服务器正常关闭时,servlet 被销毁 才会执行destory 方法。destory 是在被销毁之前执行,一般用于释放资源

Servlet 3.0:

  • 支持注解配置,可以不需要web.xml了

  • 使用方法:在创建的servlet 上 加上注解 @WebServlet("资源路径"),支持如下写法

    @WebServlet(urlPatterns = "/demo3")

    @WebServlet("/demo3")

HTTP

概念:

Hyper Text Transfer Protocol 超文本传输协议

传输协议:

定义了 客户端 和 服务器端通信时,发送数据的格式

特点:

1.基于TCP/IP的高级协议(安全的,需要经过三次握手)

2.默认端口号:80

3.基于 请求/响应 模型 (请求响应 一 一 对应,一次请求,一次响应)

4.无状态的:每次请求之间相互独立,不能交互数据

历史版本:

1.0:每一次请求,建立一次新的连接

1.1: 复用连接,对缓存性能较好

请求消息数据格式:

  1. 请求行

    • 组成格式:请求方式 请求url 请求协议/版本

      请求行: GET	/login.html	HTTP/1.1
    • HTTP协议有7种请求方式,常用的有两种请求头

      • GET

        1. 请求参数在请求行中,在url后。

        2. 请求的url长度有限制

        3. 相对安全

      • POST

        1. 请求参数在请求体中

        2. 请求的url是没有限制的

        3. 相对安全

  2. 请求头(客户端告知服务端自身信息)

    • 组成格式: 请求头名称:请求头值

      Host:localhost	当前主机
      User-Agent:浏览器告诉服务器,我访问你使用的浏览器版本信息
      //作用:服务端可以根据这个头信息区分不同浏览器解决浏览器兼容问题
      Accept:告诉服务器,浏览器可以解析哪些格式类型的信息 如 text/html
      Accept-Language: 告诉服务器所支持的语言环境
      Accept-Encoding: 告诉服务器所支持的压缩格式
      Referer:http://localhost:8080/login.html 告诉服务器,当前请求从那里来
      Referer的常用场景: 1.防盗链 2.统计工作
      Connection: keep-alive 连接状态 保持活跃服用
      Upgrade-Insecure-Requests:1 浏览器升级信息
  3. 请求空行

    • 空行,用于用于分割POST请求的请求头,和请求体的
  4. 请求体

    • 封装POST请求消息的请求参数 username=zhangsan

响应消息数据格式:

服务端发送给客户端的数据

1.响应行

  1. 格式:协议/版本 响应状态码 状态码描述
HTTP/1.1 200 OK

响应状态码:服务器告诉客户端本次请求和响应的一个状态

  • 状态码都是三位数字:

    1xx: 服务器接收客户端消息,但是没有接收完成,等待一段时间后,发送1xx 状态码询问客户端

    2xx: 成功。代表: 200

    3xx: 重定向。

        302(重定向) 服务器响应状态码302 并给客户端响应一个地址,客户端收到302状态码后自动请求对应地址进行重定向。
    
        304 (访问缓存)例如请求访问图片等大二进制数据时较慢,当浏览器请求服务器,服务器返回图片资源时,浏览器会进行图片的本地缓存,当再次访问时,如果服务器资源没有变化,
    并且当前浏览器存在图片缓存,可返回状态码 304 告知浏览器访问缓存,提高交互体验。

    4xx: 客户端错误

         404 客户端访问路径错误,未找到访问资源 -
    
         405 请求方式没有对应的 doXXX 方法

    5xx: 服务端错误

         500 服务器内部异常

2.响应头

1.格式: 头名称:值

2.常见的响应头:
Content-Type: 服务器告诉客户端本次响应体数据格式及编码格式
Content-Length: 响应字节长度
Date: 响应时间
Content-disposition: 服务器告诉客户端以什么格式打开响应体数据
默认值: in-line 在当前页面打开
attachment;filename 以附件形式打开响应体,文件下载中使用

3.响应空行

4.响应体

响应体就是传输的响应数据,有整个页面的解析响应,也有二进制数据

Request

request对象和response对象的原理

  1. request对象和response对象是由服务器创建的,我们来使用它
  2. request对象是来获取请求消息,response对象是来设置响应消息的

Request对象继承体系结构

  • ServletRequest -- 接口

    • HttpServletRequest -- 子接口(继承 ServletRequest)

      • org.apache.catalina.connector.RequestFacade 类(Tomcat)

request功能:

1.获取请求消息数据

1.获取请求行数据

*GET /day14/demo1?name=zhangsan HTTP/1.1

  • 获取请求方式:GET

    String getMethod();
  • 获取虚拟目录:/day14

    String getContextPath();
  • 获取servlet路径:/demo1

    String getServletPath();
  • 获取get方式请求参数:name=zhangsan

    String getQueryString();
  • 获取请求的URI:/day14/demo1

    //URI:	统一资源标识符	共和国
    //URL: 统一资源定位符 中华人民共和国
    String getRequestURI(); //URI如: /day14/demo1
    StringBuffer getRequestURL(); //URL如: http://localhost/day14/demo1
  • 获取协议及版本:HTTP/1.1

    String getProtocol();	//ServletRequest中的方法
  • 获取客户机的IP地址

    String getRemoteAddr();
2.获取请求头数据
  • String getHeader(String var1);	//通过请求头的名称获取请求头的值
  • Enumeration<String> getHeaders(String var1);	//获取所有的请求头名称

根据所有请求头获取所有的请求头对应的值:

@Override
protected void doGet(HttpServletRequest request, HttpServletResponse resp) throws ServletException, IOException {
Enumeration<String> names = request.getHeaderNames();
while(names.hasMoreElements()){
String s = names.nextElement();
System.out.println(s +":" + request.getHeader(s));
}
}
  • 判断浏览器版本,根据请求头 1user-agent的值来判断 是否包含指定浏览器关键字,比如Chrome

    user-agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36
  • 判断请求来源,用于防盗链 可根据请求头 referer的值来判断

    referer:http://localhost:8080/hello.html
3.获取请求体数据
请求体:只有POST请求,才有请求体,在请求体中封装了POST请求的请求参数

步骤:

1.获取流对象

BufferedReader getReader()		//获取字符输入流,只能操作字符数据

获取页面表单提交数据

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取请求消息体-请求参数
//1.获取字符流
BufferedReader br = request.getReader();
//2.读取数据
String line = null;
while ((line = br.readLine()) != null){
System.out.println(line);
}
}

2.再从流对象中拿数据

ServletInputStream getInputStream()	//获取字节输入流 可以操作所有类型数据 文件上传中使用
4.其他功能
1.获取请求参数通用方式:

不论是get 还是post 请求方式都可以使用下列方法来获取请求参数

  1. String getParameter(String name) 根据请求参数获取参数值 username=zhangsan&password=123

    @WebServlet("/demo5")
    public class ServletDemo5 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    //post获取请求参数
    String username = request.getParameter("username");
    System.out.println("post");
    System.out.println(username); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    //get获取请求参数
    String username = request.getParameter("username");
    System.out.println("get");
    System.out.println(username);
    }
    }
  2. String[] getParameter(String name) 根据请求参数获取参数值的数组 hobby=study&hobby=eat

  3. Enumeration getParameterNames() 获取所有请求的参数名称

  4. Map<String, String[]>getParameterMap()获取所有的请求参数和值 封装为一个Map返回

    技巧:获取前台参数时,可创建一个Java Bean对象,然后使用BeanUtils.populate(Object obj,Map map)

     	将map集合的键值信息,封装到 Java Bean对象中

获取请求参数乱码问题

  1. get方式:Tomcat 8 已经将get方式乱码问题解决

  2. post方式:会乱码

    解决方法:获取参数前设置一下流的编码,编码来自于请求提交页面编码格式

    request.setCharacterEncoding("utf-8");
2.请求转发:

一种在服务器内部资源跳转的方式

步骤:

		1.通过 request 对象获取请求转发器对象,RequestDispatcher getRequestDispatcher(String var1);	

		2.使用	RequestDispatcher 来进行转发:forward(ServletRequest var1, ServletResponse var2)

特点:

		1.浏览器地址栏不发生变化

		2.转发只能转发当前服务器的内部资源,比如不能转发到访问百度

		3.转发是一次请求
3.共享数据
域对象:一个有作用范围的对象,可以在范围内共享数据。

request域:代表一次请求,一般用于一次请求转发的访问多个资源中共享数据

方法:
1.setAttribute(String name,Object obj); 存储对象
2.Object getAttribute(String name); 通过键获取值
3.removeAttribute(String name); 通过键移除值
4.获取ServletContext
ServletContext getServletContext()

Response

功能:设置响应消息

1.设置响应行

1.格式:HTTP /1.1 200

 一般是设置响应状态码   :setStatus(int sc)

2.设置响应头

	setHeader(String name, String value)

3.响应体

使用步骤:

1.获取输出流

	1.字符输出流:PrintWriter getWriter()

	2.字节输出流:ServletOutputStream getOutputStream()

2.使用输出流,将数据输出到客户端浏览器

案例:

1.完成重定向

重定向是客户端浏览器访问服务器时,服务器响应客户端访问重定向另一个资源进行处理的过程

第一种方式:
设置响应状态码为302 response.setStatus(302);
设置响应头location 值为要跳转的路径 response.setHeader("location","/demo7"); 第二种方式:
使用response对象的重定向方法: response.sendRedirect("/demo7");

重定向特点

1.地址栏发生变化

2.重定向可以访问其他站点服务器资源

3.重定向是多次请求,不能使用request域对象来共享数据

路径分类

  1. 相对路径:通过相对路径不可以确定唯一资源

    • 如:./index.html
    • 相对路径通常以 . 开头
    • 写法规则:先确定当前资源和访问的目标资源之间的相对位置关系
    • ./ 代表当前目录,也可省略 , ../ 代表上一级目录

    相对目录路径写法举例:

    比如一个项目,有个路径为 demo1的 Servlet,访问路径为::

    http://localhost:8080/project/demo1 -> 目标资源

    项目的web目录下有个index.html,访问路径为:

    http://localhost:8080/project/index.html -> 当前资源

    当前资源和目标资源在同一个层级上,那么从 index.html访问 demo1的路径通常为

    <a href="./demo1">访问Demo1</a> 当前目录的 ./ 是可以省略的,所以也可以简写为:demo1

  2. 绝对路径:通过绝对路径可以确定唯一资源

2.服务器输出字符数据到浏览器

  1. 获取字符输出流,这个流是获取的不是创建的,说明流向是到客户端浏览器的

  2. 输出数据

     //1.获取字符输出流
    PrintWriter pw = response.getWriter();
    //2.输出数据
    pw.write("hello world");
    • 乱码问题:

      1. 设置输出流的默认编码及设置响应头,服务器告诉客户端本次响应体数据格式及编码格式

        //设置流的默认编码
        response.setCharacterEncoding("utf-8");
        //设置一下响应头 Content-Type: 服务器告诉客户端本次响应体数据格式及编码格式
        response.setContentType("text/html;charset=utf-8");

3.服务器输出字节数据到浏览器

  1. 获取字节输出流,可以输出任意类型字节数据,如果输出是中文,同样需要注意编码一致问题。

    //1.获取字节输出流
    ServletOutputStream outputStream = response.getOutputStream();
    //2.输出数据
    outputStream.write("hello world".getBytes());

4.验证码

验证码的实现非常简单,就是后台生成随机数,然后通过画笔画出验证码图案进行填充,实现如下:

  1. html表单

    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <title>注册页面</title>
    </head>
    <body>
    <img src="/CheckCodeServlet" id="checkCode" />
    <a id="changeCheckCode" href="#" onclick="clickChangeCheckCode()">看不清,换一张?</a>
    </body> <script>
    /**
    * 分析:点击超链接或者图片,需要换一张
    * 1.给超链接和图片绑定单机事件
    * 2.重新设置的src属性值
    */
    window.onload = function () {
    //1.获取图片对象
    var img = document.getElementById("checkCode");
    //2.绑定单机事件
    img.onclick = function () {
    //加时间戳,防止浏览器缓存,验证码刷新重复问题
    var date = new Date().getTime();
    img.src = "/CheckCodeServlet?"+date;
    }
    } function clickChangeCheckCode() {
    var img = document.getElementById("checkCode");
    var date = new Date().getTime();
    img.src="/CheckCodeServlet?"+date;
    } </script>
    </html>
  2. 验证码生成Servlet

    package org.taoguoguo.web;
    
    import javax.imageio.ImageIO;
    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 java.awt.*;
    import java.awt.image.BufferedImage;
    import java.io.IOException;
    import java.util.Random; /**
    * 验证码生成Servlet
    */
    @WebServlet(name = "CheckCodeServlet", value = "/CheckCodeServlet")
    public class CheckCodeServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    //1.创建一个图像缓冲区对象
    int width = 100;
    int height = 50;
    BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
    //2.美化图片
    //2.1填充背景色
    Graphics graphics = image.getGraphics(); //画笔对象
    graphics.setColor(Color.pink); //设置画笔颜色
    graphics.fillRect(0,0,width,height);
    //2.2画边框
    graphics.setColor(Color.blue);
    graphics.drawRect( 0,0,width-1,height-1);
    //2.3写验证码
    String code = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz123456789";
    //生成随机角标
    Random random = new Random();
    for (int i=1; i<=4; i++){
    int index = random.nextInt(code.length());
    //获取字符
    char c = code.charAt(index);
    graphics.drawString(String.valueOf(c),width/5*i,height/2);
    }
    //2.4画干扰线
    graphics.setColor(Color.green);
    for(int i=0; i<10; i++){
    int x1 = random.nextInt(width);
    int y1 = random.nextInt(height); int x2 = random.nextInt(width);
    int y2 = random.nextInt(height);
    graphics.drawLine(x1,y1,x2,y2);
    } //3.将图片输出到页面展示
    ImageIO.write(image,"jpg",response.getOutputStream());
    } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    this.doPost(request,response);
    }
    }

ServletContext对象

概念:

代表整个 web 应用,可以和程序的容器(服务器)来进行通信

获取

  1. 通过request对象获取

    request.getServletContext();
  2. 通过HttpServlet获取

    this.getServletContext();

注意:ServletContext 既然代表整个应用,那么它只有唯独一份,无论通过哪种方式获取,都是同一个对象。

举例:

//1.通过request对象获取
ServletContext context1 = request.getServletContext();
//2.通过HttpServlet获取
ServletContext context2 = this.getServletContext(); System.out.println(context1);
System.out.println(context2); System.out.println(context1 == context2); 输出:
org.apache.catalina.core.ApplicationContextFacade@586b90db
org.apache.catalina.core.ApplicationContextFacade@586b90db
true

功能

  1. 获取 MIME 类型

    MIME类型:在互联网通信过程中定义的一种文件数据类型

    格式:大类型/小类型 比如: text/html image/jpeg

    用途:响应返回时,设置响应头 Content-Type 告知浏览器用对应的引擎来解析返回数据

    获取方式

    String getMimeType(String var1);

    举例:

    public class ServletContextDemo2 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    ServletContext context = this.getServletContext();
    String fileName = "a.jpg";
    String mimeType = context.getMimeType(fileName);
    System.out.println(mimeType);
    } 输出:
    image/jpeg
  2. 域对象:共享数据

    范围:所有用户所有请求数据

    1. setAttribute(String name, Object value);
    2. getAttribute(String name);
    3. removeAttribute(String name);
    /*
    * 在Servlet1中存储值,在Servlet2中获取值
    */
    @WebServlet("/servletContextDemo1")
    public class ServletContextDemo1 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    ServletContext context = this.getServletContext();
    context.setAttribute("zhangsan","token123456");
    } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    this.doPost(request,response);
    }
    } package org.taoguoguo.web.servletcontext; import javax.servlet.ServletContext;
    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 java.io.IOException; /**
    * @author taoGG
    * @description
    * @create 2020-10-28 22:21
    */
    @WebServlet("/servletContextDemo2")
    public class ServletContextDemo2 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    ServletContext context = this.getServletContext();
    String str = (String) context.getAttribute("zhangsan");
    System.out.println(str);
    } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    this.doPost(request,response);
    }
    } 输出:
    token123456
  3. 获取文件的真实(服务器)路径

    package org.taoguoguo.web.servletcontext;
    
    import javax.servlet.ServletContext;
    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 java.io.IOException; /**
    * @author taoGG
    * @description
    * @create 2020-10-28 22:21
    */
    @WebServlet("/servletContextDemo1")
    public class ServletContextDemo1 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    ServletContext context = this.getServletContext();
    String realPath1 = context.getRealPath("/b.txt"); // web目录下
    String realPath2 = context.getRealPath("/WEN-INF/a.txt"); // WEB-INF目录下的资源访问
    String realPath3 = context.getRealPath("/WEB-INF/calsses/a.txt"); //src目录下 System.out.println("web目录下:" + realPath1);
    System.out.println("WEB-INF下" + realPath2);
    System.out.println("src目录下" + realPath3); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    this.doPost(request,response);
    }
    }
  4. 文件下载案例

    1. download.html

      <!DOCTYPE html>
      <html lang="en">
      <head>
      <meta charset="UTF-8">
      <title>文件下载</title>
      </head>
      <body>
      <a href="/downloadServlet?fileName=001.jpg">图片下载</a>
      <a href="/downloadServlet?fileName=代码.jpg">图片(中文名称)下载</a>
      </body>
      </html>
    2. DownloadServlet

      package org.taoguoguo.web.download;
      
      import utils.DownLoadUtils;
      import javax.servlet.ServletContext;
      import javax.servlet.ServletException;
      import javax.servlet.ServletOutputStream;
      import javax.servlet.annotation.WebServlet;
      import javax.servlet.http.HttpServlet;
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletResponse;
      import java.io.FileInputStream;
      import java.io.IOException; /**
      * @author taoGG
      * @description
      * @create 2020-10-28 23:14
      */
      @WebServlet("/downloadServlet")
      public class DownloadServlet extends HttpServlet {
      protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
      //1.获取请求文件名称
      String fileName = request.getParameter("fileName");
      //2.使用字节输入流加载文件进入内存
      //2.1找到文件在服务器的真实路径
      ServletContext context = this.getServletContext();
      String realPath = context.getRealPath("/img/" + fileName);
      //2.2使用字节流关联
      FileInputStream fis = new FileInputStream(realPath); //3.设置response响应头
      //3.1设置响应头类型 content-type
      String mimeType = context.getMimeType(fileName); //获取文件MIME类型
      response.setHeader("content-type",mimeType);
      //3.2解决中文文件名问题,设置响应头打开方式
      String agent = request.getHeader("user-agent");
      fileName = DownLoadUtils.getFileName(agent, fileName);
      response.setHeader("content-disposition","attachment;filename="+fileName); //4将输入流的数据写出到输出流中
      ServletOutputStream outputStream = response.getOutputStream();
      byte[] buff = new byte[1024*8]; //字节缓冲数组
      int len = 0; //读到的个数
      while ((len = fis.read(buff))!=-1){ //未读到末尾
      outputStream.write(buff,0,len); //从0开始写,写读到的长度
      }
      fis.close();
      } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
      this.doPost(request,response);
      }
      }

    中文文件名称问题

    解决思路:

    1. 获取客户端使用的浏览器版本信息
    2. 根据不同的版本信息,设置fileName的编码方式不同

Java Web专题攻关的更多相关文章

  1. Java Web专题

  2. 【原创】三分钟教你学会MVC框架——基于java web开发(2)

    没想到我的上一篇博客有这么多人看,还有几位看完之后给我留言加油,不胜感激,备受鼓励,啥都别说了,继续系列文章之第二篇.(如果没看过我第一篇博客的朋友,可以到我的主页上先浏览完再看这篇文章,以免上下文对 ...

  3. Java Web 高性能开发,第 2 部分: 前端的高性能

    Web 发展的速度让许多人叹为观止,层出不穷的组件.技术,只需要合理的组合.恰当的设置,就可以让 Web 程序性能不断飞跃.Web 的思想是通用的,它们也可以运用到 Java Web.这一系列的文章, ...

  4. Java Web 高性能开发,第 1 部分: 前端的高性能

    Web 发展的速度让许多人叹为观止,层出不穷的组件.技术,只需要合理的组合.恰当的设置,就可以让 Web 程序性能不断飞跃.所有 Web 的思想都是通用的,它们也可以运用到 Java Web.这一系列 ...

  5. 使用Intellij idea新建Java Web项目(servlet) 原理及初步使用

    准备 JDK       (配置JDK_HOME\bin   和 CLASSPATH)   注:JDK8下载已经需要注册了,请使用JDK11(现在是官方长期支持的版本)     对于我们新手来说,JD ...

  6. 大型Java进阶专题(一) 前言

    前言 ​ 各位读者好,本系列为Java进阶专题,为那些有一定工作经验,做了多年业务的码农,希望突破技术瓶颈,但没有形成系统的Java只是体系,缺乏清晰的提升方法和学习路径的人,比如作者本人.该课题的是 ...

  7. Java登录专题-----创建用户(一)

    Java登录专题-----创建用户(一) 我来填坑了 创建用户 入参 应该包括: 用户姓名,用户密码,用户手机号,用户所属机构 用户版本号,角色id 出参: 没有 数据结构: JavaBean    ...

  8. 高效 Java Web 开发框架 JessMA v3.5.1

    JessMA 是功能完备的高性能 Full-Stack Web 应用开发框架,内置可扩展的 MVC Web 基础架构和 DAO 数据库访问组件(内部已提供了 Hibernate.MyBatis 与 J ...

  9. 高效 Java Web 开发框架 JessMA v3.4.1

    JessMA 是功能完备的高性能 Full-Stack Web 应用开发框架,内置可扩展的 MVC Web 基础架构和 DAO 数据库访问组件(内部已提供了 Hibernate.MyBatis 与 J ...

  10. java web 之客户关系管理系统

    这个周末真的是觉得自己学会了一个比较高大上的本领,为什么这么觉得呢?那是因为星期六的时候觉得自己可以看看源码能做出来,可是让我头疼的是花费了一上午的时间还是没有弄出来,还好上天给了我机会,要是没有老师 ...

随机推荐

  1. 如何在 Windows 使用 Podman Desktop 取代 Docker Desktop

    Podman Desktop 是 Docker Desktop 的免费替代品,是本地开发使用的另一个绝佳选择.它提供了类似的功能集,同时保持完全开源,让您避免使用 Docker 产品的许可问题.在本文 ...

  2. SpringBoot AOP完美记录用户操作日志,附源码

    记录内容 接口名称 浏览器名称 操作系统 请求ip 接口入参.出参 接口耗时 .... 表结构 SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- -- ...

  3. Cannot add or update a child row: a foreign key constraint fails

    在使用Django添加用户时出现报错: 1 django.db.utils.IntegrityError: (1452, 'Cannot add or update a child row: a fo ...

  4. Java uuid生成随机32位

    import java.util.UUID; /** * @ClassName:UuidUtils * @Description:uuid工具类 * @Author:chenyb * @Date:20 ...

  5. Dubbo广播机制源码解读

    总结/朱季谦 先前在测试环境遇到过一个问题,即Dubbo广播机制,在对各个提供者节点进行广播操作过程中,存在最前面的两个节点出现异常的情况,但后边的其他节点仍能正常同步的情况.我以前就知道Dubbo的 ...

  6. .NET 9 预览版6发布

    微软发布了 .NET 9 的第 6 个预览版,此版本包括对运行时.SDK..NET MAUI.ASP.NET Core 和 C# 的更新,预览版没有包含太多新的主要功能或特性,因为已接近 .NET 9 ...

  7. 【原创软件】第2期:CAD文字快速批量替换工具CFR(CAD_FastReplace_V4)

    01 背景 由于工作需要,开发了一套CAD文字快速批量替换软件CFR.主要目的是:实现dwg文件一次性完成单对/多对词组快速批量替换. 02 主要功能特色 (1)无需打开CAD,快速实现文字批量替换. ...

  8. 如何做好一场NPS调研?

    我们在工作中经常遇到的一个词,那就是"产品NPS调研".当部分项目缺少专业的用研人员时,设计师.产品经理则经常会接受上级的要求,投身于NPS调研工作. 笔者也曾在2022年的某天突 ...

  9. 从Mybatis-Plus开始认识SerializedLambda

    从Mybatis-Plus开始认识SerializedLambda 背景 对于使用过Mybatis-Plus的Java开发者来说,肯定对以下代码不陌生: @TableName("t_user ...

  10. 02-springboot配置

    目录 1,前言 2,YAML介绍 3,获取yml配置文件内容 4,springboot的配置文件 5,springboot使用@Value实现映射 6,@PropertySource.@ImportR ...