阶段七-Day02-SpringMVC

一、Restful请求格式

1. 介绍

Rest(Representational State Transfer:表现层状态转移)是一种软件架构风格,其核心是面向资源的一种设计。何为面向资源,意思是网络上的所有事物都可以抽象为资源,而每个资源都有唯一的资源标识,对资源的操作不应该改变这些标识。

通俗讲就是每个资源都有一个url地址,而不是不同的操作有不同的url地址,比如我们对用户信息的增删改查,用户就是资源,增删改查是操作,以前我们是一个操作一个url地址,现在按照Restful的说法,url地址只能有一个。

Restful的出现同时也解决了客户端的种类多种多样造成请求的格式比较混乱的问题,Restful提供了一种统一的前后端交互的接口规范,可以更好的实现数据的交互。

2. 正常使用

以前我们来实现对用户的增删该查的时候是以操作为基础来声明URL地址的:

新增用户: http://localhost:8080/userAdd?uid=1&uname=zhangsan&age=12

修改用户: http://localhost:8080/userUpdate?uid=1&uname=zhangsan

删除用户: http://localhost:8080/userDelete?uid=1

查询用户:http://localhost:8080/userSel?uid=1

而按照Restful的格式对用户的操作应当只有一个url地址:

操作用户: http://localhost:8080/user

Restful要求在当前的url地址中直接嵌套请求数据。

新增用户: http://localhost:8080/user/1/zhangsan/12

修改用户: http://localhost:8080/user/1/zhangsan/28

删除用户: http://localhost:8080/user/1

查询用户: http://localhost:8080/user/1

但请求数据被嵌套在了请求地址中如何获取呢?不能在像以前直接在单元方法上声明形参来接收了,需要结合@PathVariable注解来获取。

/**
 * @RequestMapping注解可以接收任意请求方式的请求
 * @GetMapping("地址"):接收GET请求,一般用在查询方法上
 * @DeleteMapping("地址"):接收DELETE请求,一般用在删除方法上
 * @PostMapping("地址"):接收POST请求,一般用户在新增上
 * @PutMapping("地址"):接收PUT请求,一般用在修改上
 */
//查询用户信息
@GetMapping("/user/{id}")
public String selUser(@PathVariable Integer id){
    System.out.println("用户ID为:"+id);
    return "success.jsp";
}
//删除用户信息
@DeleteMapping("/user/{id}")
public String delUser(@PathVariable Integer id){
    System.out.println("用户ID为:"+id);
    return "success.jsp";
}
//新增用户信息
@PostMapping("/user/{id}/{name}/{age}")
public String addUser(@PathVariable Integer id,@PathVariable String name,@PathVariable Integer age){
    System.out.println("id = " + id + ", name = " + name + ", age = " + age);
    return "success.jsp";
}
//修改用户信息
@PutMapping("/user/{id}/{name}")
public String updateUser(@PathVariable Integer id,@PathVariable String name){
    System.out.println("id = " + id + ", name = " + name);
    return "success.jsp";
}

3. 使用Restful显示页面

我们知道,为了提高安全性,可以把页面放入到WEB-INF中。但是放入到WEB-INF中之后,访问页面之前必须先执行控制器,可以使用Restful方式显示页面,这样可以大大减少显示页面的控制器数量。

@Controller
@RequestMapping("page")
public class PageController {

    @GetMapping("{pageName}")
    public String showPage(@PathVariable String pageName){
        return "/WEB-INF/" + pageName + ".jsp";
    }
}

二、@ResponseBody注解

1. @ResponseBody介绍

@ResponseBody注解是类或方法级注解。

当方法上添加@ResponseBody注解后,控制单元方法返回值将不再被视图解析器进行解析|不会使用转发。而是把返回值放入到响应流中进行响应。

2. 最简单使用

直接在方法上添加上@ResponseBody,Spring MVC会把返回值设置到响应流中。

package com.sh.controller;

import com.sh.pojo.Emp;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
/*
*   @ResponseBody : 控制单元添加了该注解 , 不会执行视图解析器, 将控制单元的返回值直接响应会到客户端
*           要求:
*               默认:
*                   1.控制单元只能返回String类型的数据.返回其他数据类型出现406状态码
*                   2.配合@RequestMapping(produces = "text/plain;charset=utf-8")设置响应内容类型及编码格式。
*
* */
//交给SpringMVC
@Controller
public class EmpController {

    /* 走视图解析器 */

    @RequestMapping("a1")
    public void a1(HttpServletResponse response, HttpServletRequest req){
        //什么都不做
    }

    @RequestMapping("a2")
    public String a2(HttpServletResponse response, HttpServletRequest req){
        return "index";
    }

    @RequestMapping("a3")
    public String a3(HttpServletResponse response, HttpServletRequest req) throws IOException {
        response.getWriter().print("ok");

        return "index";
    }

    @RequestMapping("a4")
    public void a4(HttpServletResponse response, HttpServletRequest req) throws IOException {
        response.setContentType("text/plain;charset=utf-8");
        Emp emp = new Emp(1, "zs", "bj", new Date());
        //自动调用了toString()方法
        response.getWriter().print(emp);
    }

    /* 添加@ResponseBody 默认只能返回String类型,其他类型返回406状态码 */

    @RequestMapping("a5")
    @ResponseBody
    public Emp a5(HttpServletResponse response, HttpServletRequest req) throws IOException {
        Emp emp = new Emp(1, "zs", "bj", new Date());
        return emp;
    }

    @RequestMapping("a6")
    @ResponseBody
    public String a6(HttpServletResponse response, HttpServletRequest req) throws IOException {
        Emp emp = new Emp(1, "zs", "bj", new Date());
        String s = emp.toString();
        return s;
    }

    @RequestMapping(value = "a7",produces = "text/plain;charset=utf-8")
    @ResponseBody
    public String a7(HttpServletResponse response, HttpServletRequest req) throws IOException {
       //返回字符串中文时会出现乱码,需要配合@RequestMapping(produces = "text/plain;charset=utf-8")设置响应内容类型及编码格式
        return "你好";
    }



}

3. 自动转换为JSON字符串

@ResponseBody注解可以把控制单元返回值自动转换为JSON字符串。主要完成下面几个事情:

(1)判断返回值是否为JavaBean、JavaBean数组、List<JavaBean类型>、Map等满足键值对的类型。

(2)如果满足键值对类型,会使用Jackson把对象转换为JSON字符串,设置到响应流中。

同时会设置响应内容类型(Content-Type)为application/json;charset=utf-8

因为Spring MVC默认使用Jackson作为JSON转换工具,所以必须保证项目中存在Jackson的依赖。

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.9.10.8</version>
</dependency>
@ResponseBody : 控制单元添加了该注解 , 不会执行视图解析器, 将控制单元的返回值直接响应会到客户端
           要求:
               默认:
                   1.控制单元只能返回String类型的数据.返回其他数据类型出现406状态码
                   2.配合@RequestMapping(produces = "text/plain;charset=utf-8")设置响应内容类型及编码格式。
               导入json依赖之后:
                   1.控制单元可以返回,JavaBean,数据[元素为JavaBean],集合{元素为JavaBean},Map
                   2.SpringMVC默认使用jack将控制单元的返回值变为json格式的字符串,
                     设置响应内容类型为application/json;charset=utf-8
                   @RequestBody:将客户端请求参数为 json ,xml 转换为 javabean。
                 需要引入相关依赖。
//交给SpringMVC
@Controller
public class EmpController {

    /* 走视图解析器 */

    @RequestMapping("a1")
    public void a1(HttpServletResponse response, HttpServletRequest req){
        //什么都不做
    }

    @RequestMapping("a2")
    public String a2(HttpServletResponse response, HttpServletRequest req){
        return "index";
    }

    @RequestMapping("a3")
    public String a3(HttpServletResponse response, HttpServletRequest req) throws IOException {
        response.getWriter().print("ok");

        return "index";
    }

    @RequestMapping("a4")
    public void a4(HttpServletResponse response, HttpServletRequest req) throws IOException {
        response.setContentType("text/plain;charset=utf-8");
        Emp emp = new Emp(1, "zs", "bj", new Date());
        //自动调用了toString()方法
        response.getWriter().print(emp);
    }

    /* 添加@ResponseBody 默认只能返回String类型,其他类型返回406状态码 */

    @RequestMapping("a5")
    @ResponseBody
    public Emp a5(HttpServletResponse response, HttpServletRequest req) throws IOException {
        Emp emp = new Emp(1, "zs", "bj", new Date());
        return emp;
    }

    @RequestMapping("a6")
    @ResponseBody
    public String a6(HttpServletResponse response, HttpServletRequest req) throws IOException {
        Emp emp = new Emp(1, "zs", "bj", new Date());
        String s = emp.toString();
        return s;
    }

    @RequestMapping(value = "a7",produces = "text/plain;charset=utf-8")
    @ResponseBody
    public String a7(HttpServletResponse response, HttpServletRequest req) throws IOException {
       //返回字符串中文时会出现乱码,需要配合@RequestMapping(produces = "text/plain;charset=utf-8")设置响应内容类型及编码格式
        return "你好";
    }

    @RequestMapping("a8")
    @ResponseBody
    public Emp a8(HttpServletResponse response, HttpServletRequest req) throws IOException {
        Emp emp = new Emp(1, "zs", "bj", new Date());
        return emp;
        //{"id":1,"uname":"zs","addr":"bj","bir":1698236241717}
    }

    @RequestMapping(value = "a11")
    @ResponseBody //将符合要求的内容转换为json,必须引入json工具类。
    public List<Emp> a11() throws IOException {
        Emp people = new Emp(1, "张三", "北京", new Date());
        Emp people1 = new Emp(2, "张三1", "北京", new Date());
        Emp people2 = new Emp(3, "张三2", "北京", new Date());

        ArrayList<Emp> list = new ArrayList<>();
        Collections.addAll(list, people, people1, people2);

        return list;
    }

    @RequestMapping(value = "a12")
    @ResponseBody //将符合要求的内容转换为json,必须引入json工具类。
    public Emp[] a12() throws IOException {
        Emp people = new Emp(1, "张三", "北京", new Date());
        Emp people1 = new Emp(2, "张三1", "北京", new Date());
        Emp people2 = new Emp(3, "张三2", "北京", new Date());

        Emp[] people3 = {people, people1, people2};

        return people3;
    }

    @RequestMapping(value = "a13")
    @ResponseBody //将符合要求的内容转换为json,必须引入json工具类。
    public Map<String, Object> a13() throws IOException {
        Map<String, Object> map = new HashMap<>();
        //使用map集合代替实体类
        map.put("id", 1);
        map.put("name", "张三");
        map.put("addr", "北京");
        map.put("bir", new Date());
        return map;
    }

    
}

5. 转换为XML文件

XML格式在一些开放平台上用的比较多。例如:微信里面很多接口都是XML格式。

在Spring MVC中支持把返回值转换为XML文件。如果还是使用jackson-databind依赖,默认只能转换返回值为类类型的控制单元,返回值为List是无法转换为XML的,同时还要求实体类上必须有@XmlRootElement,才能转换。

如果项目中所有控制单元返回值结果都希望是XML格式,可以按照下面步骤完成。

5.1 导入依赖

导入依赖时注意:

(1)不要导入jackson-databind,只导入jackson-dataformat-xml。

(2)jackson-dataformat-xml版本不要太高,和Tomcat8插件不兼容。2.9.9和Spring 5.3.x可以正确兼容。

(3)因为上面练习导入的是jackson-databind,所以需要点击Maven面板 -> Lifecycle -> Clean 清空下缓存。

<dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-xml</artifactId>
    <version>2.9.9</version>
</dependency>
5.2 编写控制单元

控制单元方法和转换为JSON时写法完全相同。

6. @RestController注解

对于页面中使用前端框架时的项目。例如页面时通过:EasyUI、BootStrap、Vue等前端框架进行编写时,客户端向服务端发送的请求都是异步Ajax(或类似Ajax的异步请求)。对于这样的项目,控制器中所有的方法都包含@ResponseBody注解

补充知识

实际开发中一般响应结果会创建一个类来接收 
例如创建一个Result类 
package com.sh.pojo;

import java.io.Serializable;

public class Result<T> implements Serializable {

    private String msg; //消息
    private int code;   //自定义的状态码 200 成功   500 失败
    private T data;        //数据

    public Result() {
    }

    public Result(String msg, int code, T data) {
        this.msg = msg;
        this.code = code;
        this.data = data;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    @Override
    public String toString() {
        return "Result{" +
                "msg='" + msg + '\'' +
                ", code=" + code +
                ", data=" + data +
                '}';
    }
}

三、@RequestBody注解

1. 介绍

@RequestBody注解底层依赖的依然是Jackson工具包,其作用是把客户端传递过来的请求体中JSON或XML数据转换为Map、类、List<类>、List<Map>等类型。

既然是转换为请求体数据,所以不能是GET类型请求(GET没有请求体),多用在POST类型的请求中。

@RequestBody注解在单体架构项目使用的不是特别多。主要用在分布式项目中多个项目之间传递数据或一些开发平台中(例如微信开发平台接口返回XML数据)

如果希望在单体架构项目中使用@RequestBody注解,需要在客户端中使用Ajax请求,刻意设置请求的内容类型(Content-Type)为JSON或XML。

2. 请求内容类型详解

在客户端中无论使用的是<form>表单,还是Ajax请求,post请求内容类型都是application/x-www-form-urlencoded,表示普通表单参数。普通表单参数接收方式和上次课讲解的参数接收方式是相同的。因为是默认请求内容类型,所以在谷歌浏览器开发者工具中有时不会特意的显示,有时会显示。

2.1 表单参数接收

普通表单写法:

<form action="/testContentType" method="post">
    编号:<input type="text" name="id"/><br/>
    姓名:<input type="text" name="name"/><br/>
    <input type="submit" value="提交"/>
</form>

谷歌开发者工具中可以看到Content-Type为application/x-www-form-urlencoded。

对于普通表单参数,使用同名参数或JavaBean接收都可以。

@Controller
public class Demo2Controller {
    // 使用多个简单数据类型接收请求参数
    @RequestMapping("/testContentType")
    public String testContentType(int id, String name) {
        System.out.println(id + "," + name);
        return "/index.jsp";
    }
//    使用JavaBean接收请求参数
    @RequestMapping("/testContentType")
    public String testContentType2(People peo){
        System.out.println(peo);
        return "/index.jsp";
    }
}
2.2 Ajax请求参数

使用Ajax请求时,默认的参数类型也是普通表单参数(Form Data)。

$.ajax({
    url:"/testContentTypeAjax",
    data:{"id":1,"name":"张三"},
    type:"post",
    success:function (data) {
        console.log(data);
    },
    dataType:"json"
});

3. 修改请求内容类型

如果希望修改请求内容类型,可以使用HTML的<form>中enctype属性或使用Ajax中contentType属性进行设置。

注意:<form>的enctype属性一般只有在文件上传时才会修改,所以希望传递特定类型请求参数内容时,都是通过Ajax进行请求。

下面演示下,请求参数内容为JSON字符串的写法。

在下面代码中有三次需要重点注意的地方:

(1)contentType:必须设置。常见取值“application/json”或"application/xml"。如果没有设置这个属性,取值默认是application/x-www-form-urlencoded,表示普通表单参数。当设置为"application/json"时,会把data取值设置到请求体中,所以服务端接收参数时就不能按照普通表单参数进行接收。

(2)data:请求参数。必须是JSON字符串类型,不能是JSON格式的对象。因为在JSON中key两次必须有双引号,所以data取值两侧用单引号包含。因为在JavaScript中字符串string类型可以使用单引号包含,也可以使用双引号包含。

(3)type:请求类型不能是get类型,因为get类型没有请求体。常用就是post类型。

$.ajax({
    url:"testContentType",
    contentType:"application/json",// 修改请求内容类型为JSON
    data:'{"id":1,"name":"张三"}',// 取值两次必须有单引号,没有单引号无效
    type:"post",// 不能是GET类型请求
    success:function (data) {
        console.log(data);
    },
    dataType:"json"
});

服务端接收请求体中包含JSON字符串的请求时,需要在参数前面添加@RequestBody。表示使用Jackson把请求体中JSON/XML格式数据转换为JavaBean或Map。

小提示:

  1. 因为一个请求只有一个请求体。控制单元参数中绝对不允许出现两个@RequestBody注解。

  2. 因为@RequestBody底层使用Jackson,所以只适用于把请求体数据转换为JavaBean或Map。绝对不能在@RequestBody后面使用String等类型接收请求体内容。也就是说,客户端把JSON或XML设置到请求体,服务端使用JavaBean或Map接收请求体数据时,才能在控制单元参数前面添加@RequestBody注解。

四、Spring MVC文件上传

1. 文件上传介绍

文件上传就是把客户端的文件上传到服务端进行保存。在文件上传时文件和其他请求参数是在请求体中进行传递。所以不支持GET类型请求。

默认的表单内容类型application/x-www-form-urlencoded不支持传递文件流。所以需要在<form>的enctype中设置enctype="multipart/form-data"才表示把文件和其他表单参数设置到请求体中。

Spring MVC的文件上传是通过MultipartResovler组件实现的。提供了两个具体的实现类

必须在Spring MVC的配置文件中配置CommonsMultipartResovler组件的Bean,同时也得在项目中导入Commons-Fileupload的依赖。

(1)客户端:

   请求方式必须是POST

   enctype必须为multipart/form-data

(2)服务端:

   必须配置MultipartResovler。否则无法解析上传文件的流数据。(<bean>的id值必须叫做multipartResovler)如果没有配置MultipartResovler不仅仅是文件流数据无法解析,连带着其他表单域数据也无法解析。因为文件流数据和表单数据都在请求体中,不解析的话,文件流数据和表单数据都接收不到。

   注意文件域的name取值,文件域必须MultipartFile类型接收。且name的取值必须和MultipartFile对象名相同。

2. 文件上传实现流程

2.1 导入依赖
<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.4</version>
</dependency>
2.2 在页面中编写文件上传代码

要设置method="post" enctype="multipart/form-data"

<%@ 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">
        姓名:<input type="text" name="name"/><br/>
        头像:<input type="file" name="photo"/><br/>
        地址:<input type="text" name="address"/><br/>
       <input type="submit" value="提交"/><br/>
    </form>
</body>
</html>
2.3 配置上传解析器bean
<!-- 文件上传时,必须配置文件解析器 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"></bean>
2.4 编写单元方法处理上传请求

我们直接在单元方法上声明形参来接收请求数据即可,普通表单数据还是直接使用键名获取即可,上传的时候解析后会被存储到MultipartFile对象中,我们声明MultipartFile类型的形参接即可,但是形参名必须和file标签的name属性值一致。然后我们在单元方法中将接收到的上传资源通过流存储到服务器的硬盘中即可。

小提示:

如果客户端就一个文件域使用一个MultipartFile对象接收就可以了。

如果客户端是多个同名的文件域使用MultipartFile 数组接收。

如果客户端是多个不同名的文件域使用多个MultipartFile对象接收就可以了。

@Controller
public class PeopleController {
    /**
     * 文件上传控制单元方法实现
     *
     * @param name    也可以使用JavaBean接收name的值
     * @param address 也可以使用JavaBean接收address的值
     * @param photo   名字必须和表单中文件域的name属性值相同
     * @return
     * @throws IOException transferTo抛出的异常,可以使用try...catch处理异常。示例中为了让代码看起来简洁直接抛出了。
     */
    @RequestMapping("/upload")
    public String upload(String name, String address, MultipartFile photo) throws IOException {
        photo.transferTo(new File("D:/images", photo.getOriginalFilename()));
        return "/upload.jsp";
    }
}

3. 生成唯一文件名

在上面代码中,保存文件名称时是使用文件上传时的名称进行保存。这样做存在一个问题:如果存在同名文件,后上传文件会覆盖之前文件内容。

所以在文件上传时都会生成一个全局唯一的文件名。常见有两种方式:

(1)时间戳+随机数

(2)UUID

//时间戳
        long l = System.currentTimeMillis();
        System.out.println(l);
        //UUID
        UUID uuid = UUID.randomUUID();
        String s = uuid.toString().replace("-","");
        System.out.println(s);

文件名是全局唯一的,但是保存时文件扩展名要和上传文件的扩展名保持一致。

  @RequestMapping("/upload")
    public String upload(String name, String address, MultipartFile photo) throws IOException {
        // 判断上传文件流是否为空。如果不为空继续执行
        if(!photo.isEmpty()) {
            // 使用UUID生成文件名称
            // String fileName = UUID.randomUUID().toString();
            // 使用时间戳+随机数生成文件名
            long timeMillis = System.currentTimeMillis();
            Random random = new Random();
            String fileName = timeMillis + "" + random.nextInt(1000);
            // 获取上传时文件名
            String oldName = photo.getOriginalFilename();
            // 获取上传时文件的扩展名
            String suffix = oldName.substring(oldName.lastIndexOf("."));
             // 获取到当前项目images目录,发布到Tomcat后的绝对路径。
            String realPath = request.getServletContext().getRealPath("/images");
            System.out.println(realPath);
            // 保存到当前项目的images目录中。
            photo.transferTo(new File(realPath,fileName + suffix));
        }
        return "/upload.jsp";
    }

5. 限制上传文件大小

在很多项目中是对上传文件做严格大小限制的。当文件太大会占用服务器存储空间。当文件太小(尤其是图片)可能显示不清晰。

在CommonsMultipartResolver中提供了setmaxUploadSize(long)方法,表示设置上传文件的大小。单位是字节byte。默认值为-1,表示无限制。

<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <property name="maxUploadSize" value="1024"></property>
</bean>

五、Spring MVC文件下载

1. 文件下载介绍

文件下载就是把服务器中的资源下载到本地。

但是需要注意的是浏览器本身作为一款软件,能够打开的文件格式比较多。

例如:.html文件、图片文件、.txt文件、.xml文件、.json文件等。当超链接访问的是浏览器本身能打开的资源。浏览器直接打开。这个特点就是响应头参数Content-Disposition控制的,其默认值为inline,表示能打开就打开,不能打开就下载。

Content-Disposition可取值有两个:

(1)inline。直接在浏览器中打开(能打开就打开,不能打开就下载)。

(2)attachment。以附件形式下载。

2. 测试inline效果

因为Content-Disposition默认值就是inline。所以不需要特殊设置。

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <a href="/images/a.png">a.png</a>
    <a href="/images/b.json">b.json</a>
    <a href="/images/c.rar">c.rar</a>
</body>
</html>

3. 测试attachment效果

如果希望所有的文件都是下载,而不是能打开则打开。可以在响应头中设置Content-Disposition参数为attachment。attachment结合filename可以设置下载文件的名称。

@RequestMapping("/download")
public void download(HttpServletRequest req, HttpServletResponse response, String filename) {
    try {
        // filename=的值就是客户端看到的下载文件名称
        response.setHeader("Content-Disposition", "attachment;filename=" + filename);
        File file = new File(req.getServletContext().getRealPath("/images"), filename);
        FileInputStream fis = new FileInputStream(file);
        ServletOutputStream os = response.getOutputStream();
        IOUtils.copy(fis, os);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

4. 文件下载中包含中文名称解决办法

如果文件下载时包含中文名称,需要保证filename=后面的内容是ISO-8859-1编码。如果filename=后面是UTF-8编码且包含中文会乱码。

改写控制器代码,需要反复进行编码转换

@RequestMapping("/download")
public void download(HttpServletRequest req, HttpServletResponse response, String filename) {
    try {
        // 因为是GET请求,所以要解决请求参数中文乱码问题
        String fileNameUtf8 = new String(filename.getBytes("iso-8859-1"), "utf-8");
        // 图片名称满足固定格式
        String newFilenameUtf8 = "来自尚学堂的"+fileNameUtf8;
        String newFilenameISO = new String(newFilenameUtf8.getBytes("utf-8"),"iso-8859-1");
        // 此处是ISO-8859-1编码的内容
        response.setHeader("Content-Disposition", "attachment;filename=" + newFilenameISO);
        // 此处必须是UTF-8解决参数乱码问题的名称
        File file = new File(req.getServletContext().getRealPath("/images"), fileNameUtf8);
        FileInputStream fis = new FileInputStream(file);
        ServletOutputStream os = response.getOutputStream();
        IOUtils.copy(fis, os);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

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

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

相关文章

【rl-agents代码学习】02——DQN算法

文章目录 Highway-env Intersectionrl-agents之DQN*Implemented variants*:*References*:Query agent for actions sequence探索策略神经网络实现小结1 Record the experienceReplaybuffercompute_bellman_residualstep_optimizerupdate_target_network小结2 exploration_polic…

(.htaccess文件特性)[MRCTF2020]你传你呢 1

题目环境&#xff1a; 不难看出是一道文件上传漏洞 上传一句话木马文件burpsuite进行抓包<?php eval($_POST[shell]);?> 命名为PHP文件格式 Repeater进行重放 尝试了其它后缀进行绕过都没有成功 通过 application/x-php内容类型&#xff0c;可以看出被识别出是PHP文件&…

模拟接口数据之使用Mock方法实现(vite)

文章目录 前言一、安装依赖mockjs 安装vite-plugin-mock 安装新增mock脚本 二、vite插件配置vite-plugin-mockvite.config.ts 引入vite-plugin-mock 三、新建mock数据新建mock目录env目录新建.env.mock文件 四、使用mock数据定义接口调用接口 如有启发&#xff0c;可点赞收藏哟…

μC/OS-II---互斥信号量管理1(os_mutex.c)

目录 背景&#xff1a;优先级反转问题互斥信号量管理互斥信号量创建互斥信号量删除互斥信号量获取/等待 背景&#xff1a;优先级反转问题 在高优先级任务等待低优先级任务释放资源时&#xff0c;第三个中等优先级任务抢占了低优先级任务。阻塞时间是无法预测的&#xff0c;可能…

【ROS导航Navigation】二 | 坐标系 | 定位 | 导航约束

目录 致谢&#xff1a;ROS赵虚左老师 一、通过里程计定位 二、通过传感器定位 三、坐标系变换 四、导航约束条件 1.硬件 2.软件 致谢&#xff1a;ROS赵虚左老师 Introduction Autolabor-ROS机器人入门课程《ROS理论与实践》零基础教程 参考赵虚左老师的实战教程 一、…

聚观早报 |英伟达发布H200;夸克发布自研大模型

【聚观365】11月15日消息 英伟达发布H200 夸克发布自研大模型 iQOO 12系列开启销售 红魔9 Pro配置细节 禾赛科技第三季度营收4.5亿元 英伟达发布H200 全球市值最高的芯片制造商英伟达公司&#xff0c;正在升级其H100人工智能处理器&#xff0c;为这款产品增加更多功能&am…

《网络协议》07. 其他协议

title: 《网络协议》07. 其他协议 date: 2022-10-07 18:24:02 updated: 2023-11-15 08:00:52 categories: 学习记录&#xff1a;网络协议 excerpt: IPv6、WebSocket、WebService&#xff08;SOAP&#xff0c;WSDL&#xff09;、HTTPDNS、FTP、邮件&#xff08;SMTP&#xff0c;…

PostgreSQL基本操作

目录 1.源码安装PostgreSQL 1.1.前置条件&#xff08;root下操作&#xff09; 1.1.1.卸载yum安装的postgresql 1.1.2.创建postgres用户 1.1.3.安装部分依赖 1.1.4.源码安装uuid 1.2.安装PostgreSQL 1.2.1.使用postgres用户管理PostgreSQL 1.2.2.下载解压postgres12源码…

SUMO道路封闭车辆绕行仿真实验【TraCI】

本文将介绍如何在 SUMO 交通模拟中动态选择车辆绕行指定道路。 绕道是城市驾驶中的常见现象&#xff0c;造成原因有很多&#xff0c;包括建筑和交通事故等。 无论出于何种原因&#xff0c;并非所有车辆都会选择避开这些道路&#xff1b; 有些人可能会毫不犹豫地直接开车过去&a…

Linux信号量以及基于环形队列的生产者消费者模型

文章目录 信号量信号量的接口初始化销毁等待信号量发布信号量 环形队列结合信号量设计模型 实现基于环形队列的生产者消费者模型Task.hppRingQueue.hppmain.cc效果对于多生产多消费的情况 信号量 信号量的本质是一个计数器 首先一份公共资源在实际情况中可能存在不同的线程可…

spring 整合 JUnit

大家好&#xff0c;本篇博客我们通过spring来整合JUnitt单元测试框架。 在之前篇章的测试方法中&#xff0c;几乎都能看到以下的两行代码&#xff1a; ApplicationContext context new ClassPathXmlApplicationContext("xxx.xml"); Xxxx xxx context.getBean(Xxx…

ctyunos 与 openeuler

ctyunos-2.0.1-220311-aarch64-dvd ctyunos-2.0.1-220329-everything-aarch64-dvd glibc python3 对应openEuler 20.03 LTS SP1

μC/OS-II---互斥信号量管理2(os_mutex.c)

目录 背景&#xff1a;优先级反转问题互斥信号量管理互斥信号量发出&#xff08;释放&#xff09;互斥信号量获取/无等待互斥信号量状态查询 背景&#xff1a;优先级反转问题 在高优先级任务等待低优先级任务释放资源时&#xff0c;第三个中等优先级任务抢占了低优先级任务。阻…

【京东API】商品详情+搜索商品列表接口

利用电商API获取数据的步骤 1.申请API接口&#xff1a;首先要在相应电商平台上注册账号并申请API接口。 2.获取授权&#xff1a;在账号注册成功后&#xff0c;需要获取相应的授权才能访问电商API。 3.调用API&#xff1a;根据电商API提供的请求格式&#xff0c;通过编程实现…

8.GC基本原理

目录 概述垃圾回收引用计数法 (Reference Counting)根可达分析算法 (GCRooting Tracing)对象引用类型强引用软引用弱引用 清除垃圾1.标记-清除算法 (Mark-Sweep)2.复制算法 (Copying)3.标记-整理算法 (Mark-Compact)分代回收 (Generational Collection) 垃圾回收器GC-串行收集器…

力扣每日一题-K个元素的最大和-2023.11.15

力扣每日一题&#xff1a;K个元素的最大和 题目链接:2656.K个元素的最大和 题目描述 代码思路 题目看完直接笑嘻了&#xff0c;还有这么容易的题。由题可知&#xff0c;第一次要找出最大值m&#xff0c;那由于把m1放回去&#xff0c;那第二次找的就是m1&#xff0c;以此类推…

DGL如何表征一张图

有关于DGL中图的构建 DGL 将有向图表示为一个 DGL 图对象。图中的节点编号连续&#xff0c;从0开始。我们一般通过指定图中的节点数&#xff0c;以及源节点和目标节点的列表&#xff0c;来构建这么一个图。 下面的代码构造了一个图&#xff0c;这个图有五个叶子节点。中心节点…

python 多线程池 CPU拉满

前言&#xff1a; 关于多线程的博客先前的博客如下&#xff1a; python线程join方法_python 线程join-CSDN博客 【精选】Python GIL锁_python gil 锁-CSDN博客 python函数运行加速_python os.listdir速度慢_两只蜡笔的小新的博客-CSDN博客 只需下面的模版即可: from multi…

CNCC 2023收官,Milvus Cloud与行业大咖共话向量数据库系统

近期,CNCC 2023 在沈阳圆满结束,紧凑、前沿的 129 场技术论坛让人印象深刻。据悉,这 129 场技术论坛涵盖人工智能、安全、计算+、软件工程、教育、网络、芯片、云计算等 30 余个方向。Zilliz 受邀参与【智能时代的大数据系统】技术论坛。 智能时代的到来,无疑给社会经济和日…

前端 vue 面试题 (一)

文章目录 v-if,v-show差别v-for和v-if虚拟dom解决什么问题vue的data为什么返回函数不返回对象比较vue&#xff0c;reactvue双向绑定原理vue虚拟dom 的diff算法vue 虚拟dom的diff算法的时间复杂度vue2与vue3的区别vue数据缓存&#xff0c;避免重复计算单页应用怎么跨页面传参vue…