SpringMVC 学习(八)之文件上传与下载

目录

1 文件上传        

2 文件下载


1 文件上传        

 SpringMVC 对文件的上传做了很好的封装,提供了两种解析器。

  • CommonsMultipartResolver:兼容性较好,可以兼容 Servlet3.0 之前的版本,但是它依赖了 commons-fileupload 这个第三方工具,所以如果使用这个,一定要添加 commons-fileupload 依赖
  • StandardServletMultipartResolver:兼容性较差,它适用于 Servlet3.0 之后的版本,它不依赖第三方工具,使用它,可以直接做文件上传

本文使用 CommonsMultipartResolver 解析器,通过上传图片进行测试

导入依赖

导入 commons-fileupload.jar 包,Maven 会自动帮我们导入它的依赖包 commons-io.jar

<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.3.1</version>
</dependency>

在 SpringMVC 配置文件中配置 CommonsMultipartResolver  解析器

设置 <mvc:resources> 标签访问静态资源

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/mvc
       https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!-- 扫描指定包下的注解 -->
    <context:component-scan base-package="com.controller"/>

    <!-- 配置访问静态资源 -->
    <!-- img 必须是在 webapp 根目录下-->
    <!-- <mvc:resources> 标签将路径 /img/** 映射到 /img/ 目录 -->
    <!-- 这意味着当客户端访问 /img/** 路径时,SpringMVC会在 /img/ 目录下寻找对应的静态资源文件-->
    <mvc:resources mapping="/img/**" location="/img/"/>

    <!-- 配置注解驱动 -->
    <mvc:annotation-driven/>

    <!-- InternalResourceViewResolver 是 SpringMVC 中用于解析和渲染内部资源视图(通常是 JSP 页面)的视图解析器。
    它根据视图名称查找并加载对应的 JSP 文件,并将模型数据传递给 JSP 进行展示 -->
    <!-- 配置 JSP 解析器 -->
    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 配置前缀 -->
        <property name="prefix" value="/WEB-INF/pages/"/>
        <!-- 配置后缀 -->
        <property name="suffix" value=".jsp"/>
        <!-- 定义模板顺序 -->
    </bean>

    <!-- 配置文件上传数据专用的解析器 -->
    <!-- 这个bean的id必须是multipartResolver,否则上传文件会报400的错误 -->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!-- 设置文件编码格式 -->
        <property name="defaultEncoding" value="utf-8"/>
        <!-- 设置最大上传大小 -->
        <property name="maxUploadSize" value="#{1024*1024}"/>
    </bean>
</beans>

创建上传文件的控制器

      文件上传是使用 CommonsMultipartFile 还是 MultipartFile 呢?相信大家在学习上传文件时,肯定见过这两个类。额。。。没见过,正好可以了解下。

CommonsMultipartFile 和 MultipartFile 是 Java 中用于处理 HTTP 多部分表单数据 (Multipart Form Data) 的类。

  • CommonsMultipartFile 是 Apache Commons FileUpload 库提供的一个类,用于处理文件上传操作。它提供了更多的功能和方法,例如获取文件名、文件内容、文件类型等信息,以及设置文件的存储位置等
  • MultipartFile 是 Spring Framework 提供的一个类,用于处理文件上传和下载操作。它是基于 CommonsMultipartFile 实现的,并添加了一些 Spring 特定的功能和方法,例如通过注解进行文件解析和绑定等


        在使用时,如果你使用的是 Spring Framework,建议使用 MultipartFile 类,因为它与 Spring 的其他功能集成得更好。如果你需要更多的文件上传功能或与其他框架集成,可以考虑使用 CommonsMultipartFile 类。

        MultipartFile 封装了请求数据中的文件,此时这个文件存储在内存中或临时的磁盘文件中,需要将其转存到一个合适的位置,因为请求结束后临时存储将被清空。在 MultipartFile 接口中有如下方法:

方法名返回值描述
getContentType()String获取文件内容的类型
getOriginalFilename()String获取文件的原名称
getName()String获取 form 表单中文件组件的名字
getInputStream()InputStream将文件内容以输入流的形式返回
getSize()long获取文件的大小,单位为 byte
isEmpty()boolean文件是否为空
transferTo(File dest)void将数据保存到一个目标文件中
@Controller
public class TestController {
    @RequestMapping("/upload")
    public String testUp(MultipartFile photo, Model model, HttpSession session) throws
            IOException {
        // 获取图片文件名
        // xxx.jpg
        String originalFilename = photo.getOriginalFilename();
        System.out.println(originalFilename);
        // 使用UUID给图片重命名,并且去掉UUID的四个"-"
        // UUID.randomUUID() 随机生成 36 位的字符串
        String fileName = UUID.randomUUID().toString().replaceAll("-", "");
        // f6522af8-3f9b-4d90-a51d-e153bdb06ec6
        //获取图片的后缀
        // lastIndexOf(".") 获得字符串中最后一个 "." 的下标
        // substring(startindex) 获得字符串中从 startindex 开始的子串,即 ".jpg"
        String extName = originalFilename.substring(originalFilename.lastIndexOf("."));
        // 拼接新的图片名称
        // f6522af83f9b4d90a51de153bdb06ec6 + .jpg
        String newFileName = fileName + extName;

        // 声明转存文件时,目标目录的虚拟路径(也就是浏览器访问服务器时,能够访问到这个目录的路径)
        // 该路径需要和 SpirngMVC 配置文件中 <mvc:resources> 标签设置的路径一致
        String virtualPath = "/img";

        // 通过 session 获得 servletContext 域对象
        ServletContext servletContext = session.getServletContext();
        // 获得 img 的绝对路径  D:\JavaWeb\SpringMVCTest\target\SpringMVCTest\img
        String photoPath = servletContext.getRealPath(virtualPath);
        System.out.println("photoPath = " + photoPath);
        // 若 D:\JavaWeb\SpringMVCTest\target\SpringMVCTest\img 不存在,则创建
        File file = new File(photoPath);
        if (!file.exists()) {
            file.mkdir();
        }

        // 图片最终路径
        // D:\JavaWeb\SpringMVCTest\target\SpringMVCTest\img + f6522af83f9b4d90a51de153bdb06ec6.jpg
        String finalPath = photoPath + File.separator + newFileName;
        System.out.println("finalPath=" + finalPath);
        // 调用transferTo()方法实现文件转存,photo 是图片,photo.transferTo 相当于将图片复制到 该路径
        photo.transferTo(new File(finalPath));

        // 为了让下一个页面能够将图片展示出来,将图片访问路径存入模型,把访问图片的路径准备好
        // servletContext.getContextPath() 获得上下文路径,即 /SpringMVCTest
        // /SpringMVCTest + /img/ + f6522af83f9b4d90a51de153bdb06ec6.jpg
        String picPath = servletContext.getContextPath() + "/img/" + newFileName;
        System.out.println("picPath = " + picPath);

        model.addAttribute("picPath", picPath);
        return "success";
    }
}

index.jsp

上传文件的表单中有三点需要注意:

  • 要点1:请求方式必须是 POST
  • 要点2:enctype 属性必须是 multipart/form-data, 以二进制的方式传输数据
  • 要点3:文件上传框使用 input 标签,type 属性设置为 file,name 属性值和控制器的形参一致或通过 @RequestParam 注解重命名 name 属性值(请求参数的知识)

表单中 enctype 属性的详细说明:

  • application/x-www=form-urlencoded:默认方式,只处理表单域中的 value 属性值,采用这种编码方式的表单会将表单域中的值处理成 URL 编码方式。
  • multipart/form-data:这种编码方式会以二进制流的方式来处理表单数据,这种编码方式会把文件域指定文件的内容也封装到请求参数中,不会对字符编码。
  • text/plain除了把空格转换为 “+” 号外,其他字符都不做编码处理,这种方式适用直接通过表单发送邮件
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
    <head>
        <title>文件上传</title>
    </head>
    <body>
        <h1>上传演示案例</h1>
        <form action="${pageContext.request.contextPath}/upload" method="post" enctype="multipart/form-data">
            请选择上传的图片:<input type="file" name="photo"/><br/>
            <input type="submit" value="上传文件"/>
        </form>
    </body>
</html>

success.jsp

        在控制器中,使用了 model 向 request 域对象传递了数据 picPath,可以在整个请求中使用该数据 ${picPath} 或 ${requestScope.picPath} (SpringMVC 域对象的知识)。要访问静态资源,可以在 SpringMVC 配置文件中设置 <mvc:resources> 标签。

<%@ page contentType="text/html;charset=UTF-8" %>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <p>上传成功!!!</p>
        <%-- 需要在 SpringMVC 配置文件中设置 <mvc:resources> 标签访问静态文件--%>
        <img src="${picPath}" width="220" height="280" alt="找不到该图片"/>
    </body>
</html>

执行过程

上传多张图片

修改 index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
    <head>
        <title>文件上传</title>
    </head>
    <body>
        <h1>上传演示案例</h1>
        <form action="${pageContext.request.contextPath}/upload" method="post" enctype="multipart/form-data">
            请选择上传的图片1:<input type="file" name="photos"/><br/>
            请选择上传的图片2:<input type="file" name="photos"/><br/>
            请选择上传的图片3:<input type="file" name="photos"/><br/>
            <input type="submit" value="上传文件"/>
        </form>
    </body>
</html>

修改控制器

MultipartFile photo  -->  List<MultipartFile> photos,input 标签中 name 属性值改为 photos

@Controller
public class TestController {
    @RequestMapping("/upload")
    public String testUp(List<MultipartFile> photos, Model model, HttpSession session) throws
            IOException {
        //用于存储发送至前台页面展示的路径
        List<String> filePathNames = new ArrayList<>();
        // 声明转存文件时,目标目录的虚拟路径(也就是浏览器访问服务器时,能够访问到这个目录的路径)

        String virtualPath = "/img";

        // 准备转存文件时,目标目录的路径。File对象要求这个路径是一个完整的物理路径。
        // 而这个物理路径又会因为在服务器上部署运行时所在的系统环境不同而不同,所以不能写死。
        // 需要调用servletContext对象的getRealPath()方法,将目录的虚拟路径转换为实际的物理路径
        ServletContext servletContext = session.getServletContext();
        // 获得 img 的绝对路径  D:\JavaWeb\SpringMVCTest\target\SpringMVCTest\img
        String photoPath = servletContext.getRealPath(virtualPath);
        System.out.println("photoPath = " + photoPath);
        // 若 D:\JavaWeb\SpringMVCTest\target\SpringMVCTest\img 不存在,则创建
        File file = new File(photoPath);
        if (!file.exists()) {
            file.mkdir();
        }

        for (MultipartFile photo : photos) {
            // 获取图片文件名
            // xxx.jpg
            String originalFilename = photo.getOriginalFilename();
            System.out.println(originalFilename);
            // 使用UUID给图片重命名,并且去掉UUID的四个"-"
            String fileName = UUID.randomUUID().toString().replaceAll("-", "");
            // f6522af8-3f9b-4d90-a51d-e153bdb06ec6
            //获取图片的后缀
            // lastIndexOf(".") 获得字符串中最后一个 "." 的下标
            // substring(startindex) 获得字符串中从 startindex 开始的子串,即 ".jpg"
            String extName = originalFilename.substring(originalFilename.lastIndexOf("."));
            // 拼接新的图片名称
            // f6522af83f9b4d90a51de153bdb06ec6 + .jpg
            String newFileName = fileName + extName;


            // 图片最终路径
            // D:\JavaWeb\SpringMVCTest\target\SpringMVCTest\img + f6522af83f9b4d90a51de153bdb06ec6.jpg
            String finalPath = photoPath + File.separator + newFileName;
            System.out.println("finalPath=" + finalPath);
            // 调用transferTo()方法实现文件转存,photo 是图片,photo.transferTo 相当于将图片复制到 该路径
            photo.transferTo(new File(finalPath));

            // 为了让下一个页面能够将图片展示出来,将图片访问路径存入模型,把访问图片的路径准备好
            // servletContext.getContextPath() 获得上下文路径,即 /SpringMVCTest
            // /SpringMVCTest + /img/ + f6522af83f9b4d90a51de153bdb06ec6.jpg
            String picPath = servletContext.getContextPath() + "/img/" + newFileName;
            System.out.println("picPath = " + picPath);
            filePathNames.add(picPath);
        }
        for(int i = 0;i < filePathNames.size(); i++) {
            model.addAttribute("picPath" + (i + 1), filePathNames.get(i));
        }
        return "success";
    }
}

修改 success.jsp


<%@ page contentType="text/html;charset=UTF-8" %>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <p>上传成功!!!</p>
        <%-- 需要在 SpringMVC 配置文件中设置 <mvc:resources> 标签访问静态文件--%>
        <div>
            <img src="${picPath1}" width="220" height="280" alt="找不到该图片"/>
            <img src="${picPath2}" width="220" height="280" alt="找不到该图片"/>
            <img src="${picPath3}" width="220" height="280" alt="找不到该图片"/>
        </div>
    </body>
</html>

运行结果

2 文件下载

文件下载控制器

        传统 Servlet 文件下载方式,需要在 HttpServletResponse response 中设置各种信息,而使用 SpringMVC 的 ResponseEntity 只需要将文件二进制主体、头信息以及状态码设置好即可进行文件下载,在易用性和简洁上更胜一筹。

@RequestMapping(value = "/download")
public ResponseEntity<byte[]> testResponseEntity(String picPath, HttpSession session) throws
        IOException {
    // 获取ServletContext对象
    ServletContext servletContext = session.getServletContext();
    // 获取文件所在目录及文件名
    // picPath=/SpringMVCTest/img/05bba37fc8bc4fdfb095f41a6d2c904a.jpg
    String direcfile = "/img" + picPath.substring(picPath.lastIndexOf("/"));
    // 获取文件绝对路径
    String realPath = servletContext.getRealPath(direcfile);
    System.out.println("realPath=" + realPath);
    File file = new File(realPath);
    // 创建输入流
    InputStream is = new FileInputStream(file);
    // 创建字节数组
    byte[] bytes = new byte[is.available()];
    // 将流读到字节数组中
    is.read(bytes);
    // 创建HttpHeaders对象设置响应头信息
    MultiValueMap<String, String> headers = new HttpHeaders();
    // 设置下载文件的名字
    headers.add("Content-Disposition", "attachment; filename=" + file.getName());
    // 设置响应状态码
    HttpStatus statusCode = HttpStatus.OK;
    // 创建ResponseEntity对象
    ResponseEntity<byte[]> responseEntity = new ResponseEntity<>(bytes, headers, statusCode);
    //关闭输入流
    is.close();
    return responseEntity;
}

修改 success.jsp


<%@ page contentType="text/html;charset=UTF-8" %>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <p>上传成功!!!</p>
        <%-- 需要在 SpringMVC 配置文件中设置 可以访问静态文件--%>
        <div>
            <img src="${picPath}" width="220" height="280" alt="找不到该图片"/>
        </div>
        <a href="${pageContext.request.contextPath}/download?picPath=${picPath}">点击下载图片</a>
    </body>
</html>

演示过程

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/414305.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux 基础之 vmstat 命令详解

文章目录 一、前言二、使用说明2.1 vmstat [delay/count/d/D/t/w]2.2.vm模式的字段 一、前言 vmstat(VirtualMeomoryStatistics&#xff0c;虚拟内存统计)是一个不错的 Linux/Unix 监控工具&#xff0c;在性能测试中除了top外也是比较常用的工具之一&#xff0c;它可以监控操作…

算法 -【螺旋矩阵】

螺旋矩阵 题目示例1示例2 分析代码 题目 一个 m 行 n 列的矩阵 matrix &#xff0c;请按照顺时针螺旋顺序 &#xff0c;返回矩阵中的所有元素。 示例1 输入&#xff1a;matrix [[1,2,3],[4,5,6],[7,8,9]] 输出&#xff1a;[1,2,3,6,9,8,7,4,5] 示例2 输入&#xff1a;matrix…

JWT基于Cookie的会话保持,并解决CSRF问题的方案

使用JWT进行浏览器接口请求&#xff0c;在使用Cookie进行会话保持传递Token时&#xff0c;可能会存在 CSRF 漏洞问题&#xff0c;同时也要避免在产生XSS漏洞时泄漏Token问题&#xff0c;如下图在尽可能避免CSRF和保护Token方面设计了方案。 要点解释如下&#xff1a; 将JWT存入…

DAY12_VUE基本用法详细版

目录 0 HBuilderX酷黑主题修改注释颜色1 VUE1.1 VUE介绍1.2 Vue优点1.3 VUE入门案例1.3.1 导入JS文件1.3.2 VUE入门案例 1.4 VUE基本用法1.4.1 v-cloak属性1.4.2 v-text指令1.4.3 v-html指令1.4.4 v-pre指令1.4.5 v-once指令1.4.6 v-model指令1.4.7 MVVM思想 1.5 事件绑定1.5.1…

Centos6安装PyTorch要求的更高版本gcc

文章目录 CentOS自带版本安装gcc 4的版本1. 获取devtoolset-8的yum源2. 安装gcc3. 版本检查和切换版本 常见问题1. 找不到包audit*.rpm包2. 找不到libcgroup-0.40.rc1-27.el6_10.x86_64.rpm 的包4. cc: fatal error: Killed signal terminated program cc1plus5. pybind11/pybi…

如何使用Fastapi上传文件?先从请求体数据讲起

文章目录 1、请求体数据2、form表单数据3、小文件上传1.单文件上传2.多文件上传 4、大文件上传1.单文件上传2.多文件上传 1、请求体数据 前面我们讲到&#xff0c;get请求中&#xff0c;我们将请求数据放在url中&#xff0c;其实是非常不安全的&#xff0c;我们更愿意将请求数…

【C语言】linux内核ipoib模块 - ipoib_ib_handle_tx_wc

一、中文注释 这个函数是用来处理 Infiniband 设备在传输完成时的回调。该回调负责释放发送队列中的缓冲区并更新网络设备统计信息。 static void ipoib_ib_handle_tx_wc(struct net_device *dev, struct ib_wc *wc) {// 通过net_device结构体获取私有数据结构struct ipoib_d…

网络安全之内容安全

内容安全 攻击可能只是一个点&#xff0c;防御需要全方面进行 IAE引擎 DFI和DPI技术--- 深度检测技术 DPI --- 深度包检测技术--- 主要针对完整的数据包&#xff08;数据包分片&#xff0c;分段需要重组&#xff09;&#xff0c;之后对 数据包的内容进行识别。&#xff08;应用…

S32 Design Studio PE工具配置TMR

配置步骤 配置内容 生成的配置结构体如下&#xff0c;在Generated_Code路径下的lpTmr.c文件和lpTmr.h文件。 /*! lpTmr1 configuration structure */ const lptmr_config_t lpTmr1_config0 {.workMode LPTMR_WORKMODE_PULSECOUNTER,.dmaRequest false,.interruptEnable tr…

数据抽取平台pydatax介绍--实现和项目使用

数据抽取平台pydatax实现过程中&#xff0c;有2个关键点&#xff1a; 1、是否能在python3中调用执行datax任务&#xff0c;自己测试了一下可以&#xff0c;代码如下&#xff1a; 这个str1就是配置的shell文件 try:result os.popen(str1).read() except Exception as …

git忽略某些文件(夹)更改方法

概述 在项目中,常有需要忽略的文件、文件夹提交到代码仓库中,在此做个笔录。 一、在项目根目录内新建文本文件,并重命名为.gitignore,该文件语法如下 # 以#开始的行,被视为注释. # 忽略掉所有文件名是 a.txt的文件. a.txt # 忽略所有生成的 java文件, *.java # a.j…

数据结构:栈和队列与栈实现队列(C语言版)

目录 前言 1.栈 1.1 栈的概念及结构 1.2 栈的底层数据结构选择 1.2 数据结构设计代码&#xff08;栈的实现&#xff09; 1.3 接口函数实现代码 &#xff08;1&#xff09;初始化栈 &#xff08;2&#xff09;销毁栈 &#xff08;3&#xff09;压栈 &#xff08;4&…

【MQ05】异常消息处理

异常消息处理 上节课我们已经学习到了消息的持久化和确认相关的内容。但是&#xff0c;光有这些还不行&#xff0c;如果我们的消费者出现问题了&#xff0c;无法确认&#xff0c;或者直接报错产生异常了&#xff0c;这些消息要怎么处理呢&#xff1f;直接丢弃&#xff1f;这就是…

ImportError: urllib3 v2.0 only supports OpenSSL 1.1.1+, currently the ‘ssl‘报错解决

安装labelme出错了 根据爆栈的提示信息&#xff0c;我在cmd运行以下命令之后一切正常了&#xff0c;解决了问题&#xff01; pip install urllib31.26.6参考网址&#xff1a;ImportError: urllib3 v2.0 only supports OpenSSL 1.1.1, currently the ‘ssl’ module is compile…

常见的socket函数封装和多进程和多线程实现服务器并发

常见的socket函数封装和多进程和多线程实现服务器并发 1.常见的socket函数封装2.多进程和多线程实现服务器的并发2.1多进程服务器2.2多线程服务器2.3运行效果 1.常见的socket函数封装 accept函数或者read函数是阻塞函数&#xff0c;会被信号打断&#xff0c;我们不能让它停止&a…

docker容器技术(3)

Docker技术编排 概述&#xff1a; docker建议我们每一个容器中只运行一个服务,因为docker容器本身占用资源极少,所以最好是将每个服务单独的分割开来但是这样我们又面临了一个问题&#xff1f; 如果我需要同时部署好多个服务,难道要每个服务单独写Dockerfile然后在构建镜像,…

【XR806开发板试用】socket客户端与虚拟机服务器通信交互测试以及终端交互

XR806 客户端准备工作。 1、连接wifi 2、创建socket连接服务器。 3、创建终端接收数据线程。 wifi_connect.c #include <stdio.h> #include <string.h> #include "wifi_device.h" #include "wifi_hotspot.h" #include "kernel/os/os.h…

桥接模式(Bridge Pattern) C++

上一节&#xff1a;适配器模式&#xff08;Adapter Pattern&#xff09; C 文章目录 0.理论1.组件2.使用场景 1.实践 0.理论 桥接模式&#xff08;Bridge Pattern&#xff09;是一种结构型设计模式&#xff0c;它的核心思想是将抽象部分与其实现部分分离&#xff0c;使它们可…

区块链智能合约开发

一.区块链的回顾 1.区块链 区块链实质上是一个去中心化、分布式的可进行交易的数据库或账本 特征: 去中心化&#xff1a;简单来说&#xff0c;在网络上一个或多个服务器瘫痪的情况下&#xff0c;应用或服务仍然能够持续地运行&#xff0c;这就是去中心化。服务和应用部署在…

死区过滤器Deadband和DeadZone区别(应用介绍)

死区过滤器的算法和详细介绍专栏也有介绍,这里我们主要对这两个模块的区别和应用场景进行详细介绍 1、死区过滤器 https://rxxw-control.blog.csdn.net/article/details/128521007https://rxxw-control.blog.csdn.net/article/details/128521007 1、Deadband和DeadZone区别…