阶段七-Day02-SpringMVC

news2024/11/20 3:37:47

一、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();
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【数学建模】(1)层次分析法(AHP)

一.层次分析法的定义 层次分析法&#xff0c;简称AHP&#xff0c;是指将与决策总是有关的元素分解成目标、准则、方案等层次&#xff0c;在此基础之上进行定性和定量分析的决策方法&#xff0c;是一种层次权重决策分析方法。 层次分析法是一种主观赋值评价方法…

淘宝/京东/拼多多三方接口调用设计方案

在为第三方系统提供接口的时候&#xff0c;肯定要考虑接口数据的安全问题&#xff0c;比如数据是否被篡改&#xff0c;数据是否已经过时&#xff0c;数据是否可以重复提交等问题 在设计三方接口调用的方案时&#xff0c;需要考虑到安全性和可用性。以下是一种设计方案的概述&a…

盒子模型-详解

一、盒子模型组成 所谓盒子模型&#xff1a;就是把HTML页面中的布局元素看作是一个矩形的盒子也就是一个盛装内容的容器。css盒子模型本质是一个盒子&#xff0c;封装周围的HTML元素&#xff0c;包括边框、外边距、内边距和实际内容。 margin:外边距 用于控制盒子与盒子之间的…

【ROS导航Navigation】一 | 概述

目录 致谢&#xff1a;ROS赵虚左老师 一、【概述】二狗子找大水法 Navigation全图 二、【SLAM】即时定位与地图构建 三、【AMCL】自适应蒙特卡洛定位 四、【Move_base】路径规划 五、【cmd_vel】运动控制 六、环境感知 致谢&#xff1a;ROS赵虚左老师 Introduction A…

多行业用户齐聚,2023 IoTDB 用户大会详细议程更新!

上周我们官宣了 2023 IoTDB 用户大会举办的消息&#xff0c;获得了多方小伙伴们积极的响应&#xff0c;作为第一次线下大会&#xff0c;我们已经开始期待与大家线下相见&#xff01; 为了回应大家对于大会内容的期待&#xff0c;我们火速把更加详细的议程“搬运”来啦~ 20 位大…

【亚马逊云科技】使用Amazon Lightsail快速建站

写在前面&#xff1a;博主是一只经过实战开发历练后投身培训事业的“小山猪”&#xff0c;昵称取自动画片《狮子王》中的“彭彭”&#xff0c;总是以乐观、积极的心态对待周边的事物。本人的技术路线从Java全栈工程师一路奔向大数据开发、数据挖掘领域&#xff0c;如今终有小成…

Webpack Bundle Analyzer包分析器

当我们需要分析打包文件dist里哪些资源可以进一步优化时&#xff0c;就可以使用包分析器插件webpack-bundle-analyzer。NPM上的介绍是使用交互式可缩放树图可视化 webpack 输出文件的大小。 我的是vue2项目。 1、webpack-bundle-analyzer插件的安装 $ npm install --save-dev…

Python数据容器(集合)

集合 1.集合的定义2.集合中常用操作4.常用功能总结5.集合的特点6.练习 思考&#xff1f; 我们目前接触到了列表、元组、字符串三个数据容器了。基本满足大多数的使用场景。为何要学新的集合类型呢&#xff1f; 通过特性分析 列表可以修改、支持重复元素且有序元组、字符串不可修…

Scala---方法与函数

一、Scala方法的定义 有参方法&无参方法 def fun (a: Int , b: Int) : Unit {println(ab) } fun(1,1)def fun1 (a: Int , b: Int) ab println(fun1(1,2)) 注意点&#xff1a; 方法定义语法 用def来定义可以定义传入的参数&#xff0c;要指定传入参数的类型方法可以写返…

【Java】线程的调度、生命周期及状态转换

&#x1f33a;个人主页&#xff1a;Dawn黎明开始 &#x1f380;系列专栏&#xff1a;Java ⭐每日一句&#xff1a;夜色难免黑凉&#xff0c;前行必有曙光 &#x1f4e2;欢迎大家&#xff1a;关注&#x1f50d;点赞&#x1f44d;评论&#x1f4dd;收藏⭐️ ​ 文章目录 一.&…

3.1 Linux 前置知识

1、硬件 我们知道&#xff0c;组成计算机的硬件主要有“主机”和“输入/输出设备”。 主机包括机箱、电源、主板、CPU&#xff08;Central Processing Unit&#xff0c;中央处理器&#xff09;、内存、显卡、声卡、网卡、 硬盘、光驱等。输入/输出设备包括显示器、键盘、鼠标…

王道数据结构课后代码题p40 6.有一个带头结点的单链表L,设计一个算法使其元素递增有序 (c语言代码实现)

这一题其实用到了直接插入排序的思想 视频讲解在这里哦&#xff1a;&#x1f447; p40 第6题 王道数据结构课后代码题 c语言代码实现_哔哩哔哩_bilibili 本题代码为 void paixu(linklist* L)//对单链表内的元素排序 {lnode* p (*L)->next;lnode* pre *L;lnode* r p-&…

「 电商API接口系列之淘宝API接口调用 」

API从技术角度来说就是应用程序编程接口。通过API我们可以直接获取一些我们需要的数据结果&#xff0c;而不需要自己编写相应的程序&#xff0c;有点类似模块化调用函数&#xff0c;大大加快了我们编程的速度。当然这个数据传输是需要网络的&#xff0c;所以一般API的形式看起来…

论文十问:ResNet(Deep Residual Learning for Image Recognition)

文章目录 1. 论文试图解决什么问题?2. 这是否是一个新的问题?3. 这篇文章要验证一个什么科学假设?4. 有哪些相关研究&#xff1f;如何归类&#xff1f;谁是这一课题在领域内值得关注的研究员&#xff1f;5. 论文中提到的解决方案之关键是什么?6. 论文中的实验是如何设计的?…

stable diffusion comfyui的api使用教程

一、为什么要使用comfyui的api?对比webui的api&#xff0c;它有什么好处&#xff1f; 1、自带队列 2、支持websocket 3、无需关心插件是否有开放api接口&#xff0c;只要插件在浏览器中可以正常使用&#xff0c;接口就一定可以使用 4、开发人员只需关心绘图流程的搭建 5、切换…

【LeetCode刷题笔记】二叉树(二)

257. 二叉树的所有路径 解题思路: 1. DFS 前序遍历 ,每次递归将 当前节点的拼接结果 传递到 下一层 中,如果当前节点是 叶子节点 ,就将 当前拼接结果 收集答案并返回。 注意:路径path结果可以使用 String 来拼接,这样可以避免回溯处理。

Git 本地库基本教程

目录 一. Git 概述 1.1 何为版本控制 1.2 为什么需要版本控制 1.3 版本控制工具 1.3.1 集中式版本控制工具 1.3.2 分布式版本控制系统 1.4 Git简介 1.5 Git工作机制 1.6 Git 和代码托管中心 1.6.1 局域网 1.6.2 互联网 二. Git 安装 三. Git…

SystemC 学习之与 System Verilog 的混合仿真(九)

1、下载 uvmc (uvm connect) https://download.csdn.net/download/yp18792574062/88529417?spm1001.2014.3001.5501 2、配置相关环境变量 export UVM_HOME${VCS_HOME}/etc/uvm export UVMC_HOME/home/yangpan/yangpan/uvmc/uvmc-2.3.1 然后执行 source ~/.zshrc 更新 3、…

让公有云服务“宁安如梦”的“定心丸”在哪里?

电视剧《宁安如梦》正在热播中&#xff0c;该剧讲述了主人公在经历人生的重大风险后&#xff0c;重获新生再活一遍&#xff0c;以确定性的方式抵御和化解原有的重大风险。然而&#xff0c;在现实的生活中&#xff0c;却没有这样的重来机会。 2023年11月13日&#xff0c;Gartne…

二、服务拆分及远程调用

目录 一、注意事项&#xff1a; 1.单一职责: 2.数据独立: 3.面向服务&#xff1a; 二、服务拆分例子&#xff1a; 三、远程调用例子&#xff1a; 微服务调用方式&#xff1a; 四、提供者与消费者 服务调用关系&#xff1a; 一、注意事项&#xff1a; 1.单一职责: 不同…