servlet、http协议、验证码、文件处理

概念

Web

静态Web

在这里插入图片描述

动态Web

在这里插入图片描述

b/s

  1. 什么是b/s结构

    客户端使用浏览器,服务端使用web服务器,客户端与服务端使用的是http协议通讯

  2. 优点

    • 无需安装(客户端使用浏览器),c/s需要安装客户端,并且版本一旦更新需要重新安装
    • 编写代码相对简单,c/s结构需要我们分别在客户端与服务端编写相应的自定义协议和通讯模块,而b/s结构使用的是标准的http协议,并且浏览器和服务器包含了通讯模块

Servlet

定义

servlet是sun公司制定的一种用于扩展web服务器功能的组件规范

  1. 扩展web服务器功能?
    早期的web服务器只能处理静态资源文件(事先写好的html),不能处理动态资源请求(需要依据请求参数后进行计算生成相应的页面),为了让这些web服务器能够处理动态资源请i去,所以需要扩展。

    ​ 早期采用cgi(common gataway interface)的技术扩展,可以使用perl,c等来开发cgi程序,但cgi程序有几个问题,第一:移植性不好,第二,编程复杂(需要自己分析请求数据),第三,性能不佳(当web服务器收到请求之后,会启动一个cgi进程来处理请求)

  2. 扩展方式?

    组件+容器

    • 什么是组件

      ​ 符合规范,实现某个特定功能,并且可以部署在容器上的软件模块。(程序员编写)

    • 什么容器

      ​ 符合规范,为组件提供运行的环境,并且可以管理组件生命周期的软件程序。

    • 优势:

      ​ 容器负责大量的基础服务(比如浏览器与服务器之间网络通讯,参数传递等等),而组件负责编写业务逻辑。组件依赖于容器,但是不依赖与特定的容器。(tomcat jboss weblogic)

编写servlet

  1. 导入jar:servlet-api.jar

  2. 定义class文件,继承HttpServle,并且重写service方法

    package web;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.io.PrintWriter;
    
    public class HelloServlet extends HttpServlet {
        @Override
        protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //向页面输出
            //设置响应消息头
            //获取字符输出流
            resp.setContentType("text/html");
            //默认编码是ISO8859-1,只能处理单字节    告诉服务器和浏览器使用utf-8编码、解析
            resp.setCharacterEncoding("utf-8");
            PrintWriter out = resp.getWriter();
    
            //获取请求参数值
            String name = req.getParameter("name");
            out.print("<span style='color:red;'>你好" + name + "</span>");
        }
    }
    
  3. 在web.xml文件中配置servlet组件

        <!--成对出现-->
        <servlet>
            <!--定义名称:同一个web.xml文件中的name值必须唯一-->
            <servlet-name>helloServlet</servlet-name>
            <!--servlet组件的完整类路径-->
            <servlet-class>web.HelloServlet</servlet-class>
        </servlet>
        <servlet-mapping>
            <!--与上方的name值一样-->
            <servlet-name>helloServlet</servlet-name>
            <!--请求路径 -->
            <url-pattern>/hello</url-pattern>
        </servlet-mapping>
    

错误界面配置

 @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        if ("/findAll".equals(url)) {
            try {
                Integer.parseInt(null);		//异常
                req.setAttribute("list", list);
                req.getRequestDispatcher("/emp/emplist.jsp").forward(req, resp);
            } catch (Exception e) {
                e.printStackTrace();
                throw new ServletException(e); //抛出给容器:Tomcat
            }
    <error-page>
        <error-code>500</error-code>
        <location>/emp/error.jsp</location>
    </error-page>
    <!--或者  二选一 -->
    <error-page>
        <exception-type>javax.servlet.ServletException</exception-type>
        <location>/emp/error.jsp</location>
    </error-page>

查找servlet

当在浏览器地址栏中输入localhost:8080/servlet01/hello?name=jack

  1. 根据ip:port与服务器建立连接
  2. 根据应用名(servlet01)查找服务器上的文件夹,每一个web应用都有一个web.xml文件
  3. 根据/hello与web.xml文件中的url-pattern进行匹配,匹配成功之后,根据servlet-name查找到对应的servlet组件(servlet-class)
  4. 调用servlet组件的service方法处理数据

获取请求参数值

url?name=jack&city=nj&city=bj
//参数名唯一:
String:request.getParameter(String 参数名);
//参数名不唯一:
String[] 参数名 = req.getParameterValues(String类型的参数名);
//注:参数名写错,返回null

原理

  1. 服务器和客户端建立连接:浏览器根据ip:port与服务器建立连接

  2. 按照http协议打包(请求数据包):浏览器将请求数据打包,打成数据包之后才能发送,其中包括若干数据头、请求地址,请求资源路径(/servlet01/hello?name=jake)等等

  3. 发送请求:浏览器向服务器发送请求

  4. 拆包:服务器根据http协议进行拆包,获取浏览器发送过来的所有请求数据

  5. 创建请求(HttpServletRequest)对象

    ​ 服务器根据获取来的信息创建request对象,并将解析后的数据放到request对象上,可以通过请对象获取数据

  6. 创建相应(HttpServletResponse)对象

    ​ 服务器通过相应对象response获取输出流,向页面输出数据

  7. 容器根据请求地址查找到servlet组件,利用反射技术创建servlet对象

  8. 容器用service方法,并且将实现创建好的request与response传递给service方法,通过request获取请求参数,进行相应的处理,然后将结果写到response对象

  9. 容器读取response对象上的数据,按照http协议打包(响应数据包)

  10. 服务器向浏览器发送响应

  11. 浏览器拆包获取服务器响应的数据,生成新的页面,返回给用户

在这里插入图片描述

常见错误

  1. 写servlet组件需要继承HttpServlet ---- 访问时抛异常,错误状态码500,错误内容 class is not servlet
  2. service 方法名不能写错 — 方法写错时,访问时抛出异常,错误状态码405,错误内容
  3. web.xml文件中的url-pattern路径不能写错,必须以“/”开头,即/xxxx
  4. web.xml文件中一组servlte与servlet-mapping中的name值需要对应,同一个xml文件中,不同组的servlet中的name以及url-pattern不能相同
  5. 访问地址: http://localhost:8080/应用名/url-pattern —写错地址,xml中没有这个地址,错误状态404

Http协议

定义

超文本传输协议:是一种应用层协议,是由W3C制定的,它定义了浏览器(客户端)与web服务器之间的通讯模块过程以及数据格式

通讯过程

  1. 建立连接:3次握手,4次挥手。浏览器根据ip以及端口号与服务器建立连接
  2. 打包:浏览器将请求数据打包发送给服务器
  3. 打包:服务器将响应数据打包发送给浏览器
  4. 断开连接
  • 如果浏览器需要再次发送请求,必须重新建立一个新的连接
  • 特点:一次请求,一次连接
  • 优点:效率高,可以使用有限的连接资源为更多的用户服务
  • 问题:一个业务需要多次交互(一个请求一次响应为一个交互)才能完成

三次握手

  • SYN(synchronous建立联机)
  • ACK(acknowledgement 确认)
  • PSH(push传送)
  • FIN(finish结束)
  • RST(reset重置)
  • URG(urgent紧急)

  1. 客户端先会发生器请求:这是第一次握手,带有序列号(syn)到服务端,此时客户端的状态为Syn-send状态而服务端在之前一直是listen状态,在被动等待接收客户端的请求信息。
  2. 当其收到客户端发起建立连接的请求之后。服务端就会向客户端返回一定带有序列号(syn),确认码(ack)的消息。而服务端的状态变为Syn-recv 。
  3. 客户端在收到服务的返回信息之后。状态变为Established ,然后向服务发生一个带有确认码(ack)的信息, 服务器收到客户端带有确认码(ack)的信息之后,状态也变为Established。

四次挥手

为了保证服务端的数据完整的传送到客户端。所以,才会有四次挥手的情况。

  1. 在客户端,服务端之间的连接建立好之后。客户端会主动向服务端发送一段带有结束码(Fin)的询问信息(大致就像问服务端,你数据发送完了吗,我要关闭连接了)给服务端这是第一次挥手;
  2. 服务端就会回应客户端一个信息(我还没有传完了,不能关闭连接),带有确认码(ack)这是第二次挥手;
  3. 当服务器传完数据之后,就会向客户端发送一个带有结束码(Fin)的消息(我数据传输完了,可以关闭连接),这是第三次挥手;
  4. 客户端收到服务端的消息之后,会返回服务一个带有确认码(ack)的消息(好的,我知道了,那就关闭连接),这是第四次挥手。这之后,整个连接就关闭了。

数据格式

请求数据包

  1. 请求行(第一行)
    包含:请求方式(GET/POST)
    请求资源路径(/servlet/hello?name=jack)
    协议类型与协议的版本号
  2. 若干消息头
    key=value
    包含:一堆键值对,由w3c制定,每一个键值对都有具体的含义,规定了浏览器与服务器之间可以通过消
    息头的方式来传递一些特定的信息
    重点:user-agent:包含了浏览器相关的信息,包括浏览器的类型与版本号
  3. 实体内容
    GET请求,此处是空的,GET请求中的实体内容在请求行的请求资源路径中
    post请求,此处是参数名=参数值&参数名=参数值

响应数据包

  1. 响应行
    包含:协议类型与协议版本
    响应的状态码
    状态码的描述信息
  2. 若干消息头
    包含:一堆键值对,由w3c制定,服务器也可以发送一些消息头给浏览器,比如content-type告诉浏览
    器,服务器返回的数据类型以及编码格式
  3. 实体内容
    response对象中的数据

get和post

哪些是get、psot

  1. get请求
    • 直接在浏览器地址栏输入请求地址
    • 点击链接<a href=""></a>
    • 表单的默认提交方式
  2. post请求
    • 将表单的提交方式修改为post:method=“post”

区别

  1. get的请求参数放在请求资源路径后面,不能提交大量的数据(不同的浏览器对数据大小的限制不一样,范围是2-8k,一般默认<=2K);而post请求参数放在实体内容中,理论上没有限制
  2. 相对于get,post会更加安全一点,但不是绝对安全,可以通过代替服务器的方式抓取数据包的数据,因此对敏感的数据需要加密处理

重定向

什么是重定向

​ 服务器向浏览器发送一个302状态码以及一个location的消息头(值是一个url地址),但浏览器检测到302状态码之后,会立即先location的地址发送请求

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EXIm6GhA-1655108617670)(servlet.assets/image-20211106143442191.png)]

B一个web资源收到客户端A请求后,B他会通知A客户端去访问另外一个web资源C,这个过程叫重定向

实现

//url:重定向目的地
response.sendRedirect(String url);

注意事项

  1. 重定向之前,不能调用out.close()或者out.flush()方法
  2. 重定向之前,会清空response中的缓存数据

特点

  1. 重定向之后,浏览器地址栏的地址会发生改变
  2. 重定向的地址是任意的,可以跨应用和服务

转发

定义

​ 一个web组件将未完成的事情交给另外一个web组件继续完成。通常情况是,servlet组件负责取数据,jsp组件负责展示数据。(类似于带有数据的重定向)

如何转发

  req.setAttribute("list", list);	//绑定数据
  req.getRequestDispatcher("/emp/updateEmp.jsp").forward(req, resp);	//转发器转发

注意事项

  1. 转发之前,不能调用out.close()或者out.flush()方法
  2. 转发之前,会清空response中缓存数据

路径问题

a.链接<a href="del.do">删除</a>
b.表单<form atcion="add.do"></form>
c.重定向response.sendRedirect("list.do");
d.转发request.getRequestDispatcher("emplist.jsp");

--相对路径
 不以/开头的路径,比如href="del.do"
 
--绝对路径
 以/开头的路径,绝路路径写法:
 链接,表单,重定向,以/开头,从应用名开始写,比如/servlet02/del.do
 转发,以/开头,从应用名之后开始写,比如/test1.jsp

容器处理

  1. 将异常信息抛给容器
    情况一:try…catch语句之外的代码发生了异常,默认直接抛给了service方法的调用者,即容器
    情况二:try…catch语句块内的代码发生了异常,需要throw e,将异常往上抛

  2. 在web.xml文件中配置错误页面

     <error-page>
          <!-- <error-code>500</error-code>-->
          <exception-type>javax.servlet.ServletException</exception-type>
          <location>/error.jsp</location>
     </error-page>
    

转发、重定向区别

  1. 转发之后,浏览器地址栏的地址不会发生改变,而重定向会发生改变
  2. 转发只能在同一应用内部跳转,而重定向跳转的地址是任意
  3. 转发之间的组件共享request与response,而重定向不共享

servlet请求资源路径

什么是请求资源路径

​ http://localhost:8080/servlet01/hello在地址栏中输入的请求地址,端口号之后的部分就是请求资源路径,紧跟在端口号之后的部署在tomcat服务器上对应的应用名,紧跟在应用名之后是具体的应用内部组件路径

如何处理

  1. 第一种情况:
    容器根据应用名查找对应的文件夹,会先假设访问的是servlet,然后根据web.xml文件中的配置信息匹配url-pattern,匹配成功之后,调用相应的servle组件处理请求。
  2. 第二种情况:
    如果与url-pattern匹配失败,容器会认为访问的是静态资源文件,然后容器会去查找该文件,找到则返回,否则返回404
    localhost:8080/servlet01/addEmp.htm

匹配规则

  1. 精确匹配,以 / 开头 比如<url-pattern>/hello</url-pattern>
  2. 通配符匹配,以/开头,配合*定义路径 比如:/* , *:任意长度的任意字符 /abc/*
  3. 后缀匹配,不能以 / 开头,*.任意字符串 比如: *.do *.action *.aaa

一个servlet如何处理多个请求

  1. 采用后缀匹配模式

  2. 在servlet中获取请求资源路径

    /servlet01/list.do
     String:request.getRequestURI();
    

Servlet生命周期

​ 生命周期指的是servlet容器如何创建servlet组件对象,为其分配资源,调用其方法以及销毁其实例

的整个过程。

实例化

默认情况下,当请求到达容器时,容器会先检查容器当中是否有该对象,如果没有,则创建该servlet组件对象,如果有,则不创建。

通过<load-on-startup>1</load-on-startup>配置可以提前实例化servlet对象,参数值>=0整数,数值越小,优先级越高

初始化

容器为servlet分配资源,调用init(ServletConfig config)方法
init方法只会被调用一次,GenericServlet重写了init方法,该方法会将容器创建好的ServletConfig
对象赋值给当前类的成员变量,便于子类获取ServletConfig对象,通过该对象的
getInitParameter(String name)方法可以获取初始化参数值。
配置初始化参数:

<init-param>
<param-name>version</param-name>
<param-value>1.0</param-value>
</init-param>

可以覆盖父类的init方法,init(ServletConfig config)或者init(),建议重写后者

就绪

容器收到了客户端的请求,开始调用service(ServletRequest req,ServletResponse res)方法,
service方法会被调用多次,可以覆盖
方式一:覆盖service(HttpServletRequest req,HttpServletResponse res)
方式二:根据请求方式覆盖doGet()或者doPost()方法
建议选择前者

销毁

容器会根据自身的算法,将不再使用的servlet组件对象销毁,销毁之前,调用destroy方法

总结

在servlet生命周期整个过程中,init与destory方法只会被调一次,而service方法会被调用多次

相关的抽象类与接口

//1.Servlet接口
init(ServletConfig config);
service(ServletRequest req,ServletResponse req);
destroy();
//2.GenericServlet抽象类,实现了Servlet接口,重写了
init(ServletCOnfig config);
destory();
//3.HttpServlet抽象类,继承了GenericServlet,重写了
service(ServletRequest req,ServletResponse req);

在这里插入图片描述

ServletContext

定义

上下文对象,容器会为每一个web应用创建一个唯一的servletContext对象,该对象一直存在,除非应用被销毁

方法

添加、获取数据

//添加数据
setAttribute(String name,Object obj);
//获取数据	返回Object类型
getAttribute(String name);

获取全局参数

getInitParameter(String name);	//返回String类型

获取物理路径

getRealPath(String url);	//返回String类型

配置初始化参数

 <!--配置一些web应用初始化参数-->
  <context-param>
    <param-name>url</param-name>
    <param-value>jdbc:mysql://localhost:3306/mybatis</param-value>
  </context-param>

获取上下文对象

GenericServlet.getServletContext();
HttpSession.getServletContext();
ServletConfig.getServletContext();

线程安全

​ 当请求到达容器,容器会启动一个线程处理该请求,多个请求到达容器时,就会有多个线程调用同一个servlet组件处理,此时,可能会有线程安全问题。

解决方案:

  • 加锁,可以在方法上加锁,也可以在有线程安全问题的代码块上加锁,建议后者
  • servlet实现SingleThreadModel接口,容器会为每一个请求创建一个新的servlet对象(对资源空间占用较大,不建议)
  • 尽量避免使用成员变量
  • 使用ThreadLocal封装变量的值,每一个线程会创建一个变量的副本
Servlet{
      int num = 1
          service(){
            if(num>0){
                  num--
           }   
     }
}

//线程A  num>0  num--
//线程B  num>0  num--

存放数据的对象

生命周期比较:pageContext<request<session<application

  • pageContext:只能当前页面才能使用
  • request:一次交互期间
  • session:一次会话期间
  • application:只要应用不关闭,其他组件都可以获取该对象

手动抛出异常

throw new UserNameExpetion("账号错误");
public class UserNameExpetion extends RuntimeException {
    public UserNameExpetion(String massage) {
        super(massage);
    }
}
        try {
            
        } catch (UserNameExpetion e) {
            System.out.println(e.getMessage());
        } catch (Exception e) {
            e.printStackTrace();
        }

验证码

随机生成验证码

方式一:

package servlet;

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;
import java.util.stream.IntStream;

@WebServlet("/chechCode")
public class CheckCodeServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //设置响应的编码格式
        resp.setContentType("image/jpeg");
        //创建画纸
        BufferedImage image = new BufferedImage(60, 20, BufferedImage.TYPE_INT_RGB);
        //创建画笔
        Graphics g = image.getGraphics();
        //设置画笔的颜色,随机颜色
        Random r = new Random();
        g.setColor(new Color(r.nextInt(256), r.nextInt(256), r.nextInt(256)));
        g.fillRect(0, 0, 60, 20);
        //添加线
        IntStream.range(0, 5).forEach(num -> {
            g.setColor(new Color(r.nextInt(256), r.nextInt(256), r.nextInt(256)));
            g.drawLine(r.nextInt(60), r.nextInt(20), r.nextInt(60), r.nextInt(20));
        });
        //更换颜色,准备写字
        g.setColor(Color.black);
        g.setFont(new Font(null, Font.BOLD, 15));
        String num = makeNun(5);
        g.drawString(num, 5, 15);
        //输出图片
        ImageIO.write(image, "jpeg", resp.getOutputStream());
    }

    private String makeNun(int length) {
        String str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789";
        StringBuilder builder = new StringBuilder();
        Random random = new Random();
        IntStream.range(0, length).forEach(i -> {
            char c = str.charAt(random.nextInt(str.length()));
            builder.append(c);
        });
        return builder.toString();
    }
}

方式二:使用第三方jar(效果不好看)

jar:ValidateCode.jar

package servlet;

import cn.dsna.util.images.ValidateCode;

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;

@WebServlet("/chechCode2")
public class CheckCodeServlet2 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ValidateCode code = new ValidateCode(60, 20, 5, 5);
        code.write(resp.getOutputStream());
    }
}

文件上传

  1. 导入jar

    commons-io-2.6.jar

    commons-fileupload-1.4.jar

  2. 代码实现

package servlet;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

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.File;
import java.io.IOException;
import java.util.List;
import java.util.UUID;

@WebServlet("/upload")
public class UploadFileServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //提供了解析器缺少的一些配置,比如临时目录的设置
        DiskFileItemFactory factory = new DiskFileItemFactory();
        //创建解析器
        ServletFileUpload upload = new ServletFileUpload(factory);
        try {
            //解析,将表单中的数据转换成一个个FileItem对象,每一个FileItem对象对应一个文本域
            List<FileItem> fileItems = upload.parseRequest(req);
            //遍历
            for (FileItem fileItem : fileItems) {
                //判断是否是普通的文本域
                if (fileItem.isFormField()) {
                    //设置编码格式
                    String username = fileItem.getString("utf-8");
                    System.out.println(username);
                } else {
                    //不是普通的文本域,将文件写到执行的目录
                    //获取上传到文件夹的目录
                    String path = getServletContext().getRealPath("/upload");
                    //获取源文件名称
                    String originalName = fileItem.getName();
                    String fix = originalName.substring(originalName.lastIndexOf("."));
                    //防止文件名重复
                    String newName = UUID.randomUUID() + fix;
                    //创建文件,写入数据
                    File file = new File(path + "\\" + newName);
                    fileItem.write(file);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<form action="/upload" method="post" enctype="multipart/form-data">
    photo:<input type="file" name="photo"/><br/>
    <input type="submit" value="上传">
</form>
</body>
</html>

文件下载

思路

  1. 获取下载文件的路径
  2. 下载的文件名是什么
  3. 想办法让浏览器支持我们下载
  4. 获取下载文件的输入流
  5. 创建缓冲区
  6. 获取OutputStream对象
  7. 将FileOutputStream流写入buffer缓冲区,并且将数据输出到客户端

代码实现

package servlet;

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;
import java.net.URLEncoder;

@WebServlet("/down")
public class DownFileServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1. 获取下载文件的路径
        String realPath = "F:\\WP\\IDEA_File\\Servlet\\upload\\图片.png";
        
        //2. 下载的文件名是什么:截取
        String fileName = realPath.substring(realPath.lastIndexOf("\\") + 1);
        
        //3. 想办法让浏览器支持(Content-Disposition)我们下载
        //attachment:附件 URLEncoder.encode:设置文件名的编码方式,否则中文乱码
        resp.setHeader("Content-Disposition",
                " attachment; filename = " + URLEncoder.encode(fileName, "UTF-8"));
        
        //4. 获取下载文件的输入流
        FileInputStream in = new FileInputStream(realPath);
        
        //5. 创建缓冲区
        int len = 0;
        byte[] buffer = new byte[1024];
        
        //6. 获取OutputStream对象
        ServletOutputStream out = resp.getOutputStream();
        
        //7. 将FileOutputStream流写入buffer缓冲区,并且将数据输出到客户端
        while ((len = in.read(buffer)) != -1) {
            out.write(buffer, 0, len);
        }
        in.close();
        out.close();
    }
}

Path.lastIndexOf(“\”) + 1);

    //3. 想办法让浏览器支持(Content-Disposition)我们下载
    //attachment:附件 URLEncoder.encode:设置文件名的编码方式,否则中文乱码
    resp.setHeader("Content-Disposition",
            " attachment; filename = " + URLEncoder.encode(fileName, "UTF-8"));
    
    //4. 获取下载文件的输入流
    FileInputStream in = new FileInputStream(realPath);
    
    //5. 创建缓冲区
    int len = 0;
    byte[] buffer = new byte[1024];
    
    //6. 获取OutputStream对象
    ServletOutputStream out = resp.getOutputStream();
    
    //7. 将FileOutputStream流写入buffer缓冲区,并且将数据输出到客户端
    while ((len = in.read(buffer)) != -1) {
        out.write(buffer, 0, len);
    }
    in.close();
    out.close();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值