Thymeleaf 使用

文件上传

文件上传的必要前提

  • form 表单的 enctype 取值必须是:multipart/form-data (默认值是:application/x-www-form-urlencoded) enctype:是表单请求正文的类型
  • method 属性取值必须是 Post
  • 提供一个文件选择 <input type="file" name=""/>

单个文件上传

controller

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.util.UUID;

/**
 * @author 贾宇光
 * @version 1.0
 * @date 2020/7/8 10:11
 */
@Controller
public class FileController {
    @GetMapping("file")
    public String file(HttpServletRequest request) {
        System.out.println("本项目路径:" + System.getProperty("user.dir"));
        System.out.println("tomcat路径:" + request.getSession().getServletContext().getRealPath("/uploads"));
        return "MultipartFile";
    }

    @RequestMapping(value = "/fileupload")
    @ResponseBody
    public String fileupload2(@RequestParam("upload") MultipartFile upload,
                              HttpServletRequest request) throws Exception {
        System.out.println("文件名称:" + upload.getOriginalFilename());
        System.out.println("文件是否是js为后缀:" + upload.getOriginalFilename().endsWith(".js"));
        // 先获取到要上传的文件目录
        // String path = request.getSession().getServletContext().getRealPath("/uploads");
        String path = System.getProperty("user.dir") + "/uploads";
        System.out.println(path);
        // 创建File对象,一会向该路径下上传文件
        File file = new File(path);
        // 判断路径是否存在,如果不存在,创建该路径
        if (!file.exists()) {
            file.mkdirs();
        }
        // 获取到上传文件的名称
        String filename = upload.getOriginalFilename();
        String uuid = UUID.randomUUID().toString().replaceAll("-", "").toUpperCase();
        // 把文件的名称唯一化
        filename = uuid + "_" + filename;
        // 上传文件
        upload.transferTo(new File(file, filename));
        System.out.println(filename);
        return "success";
    }
}

前端页面

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>MultipartFile</title>
</head>
<body>
    <h3>文件上传</h3>
    <form th:action="@{/fileupload}" method="post" enctype="multipart/form-data">
        选择文件:<input type="file" name="upload"/><br/>
        <input type="submit" value="上传文件"/>
    </form>
</body>
</html>

多个文件上传

controller

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.util.ArrayList;
import java.util.UUID;

/**
 * @author 贾宇光
 * @version 1.0
 * @date 2020/7/8 10:11
 */
@Controller
public class FileController {
    @GetMapping("file")
    public String file(HttpServletRequest request) {
        System.out.println("本项目路径:" + System.getProperty("user.dir"));
        System.out.println("tomcat路径:" + request.getSession().getServletContext().getRealPath("/uploads"));
        return "MultipartFile";
    }

    @RequestMapping(value = "/fileupload")
    @ResponseBody
    public String fileupload2(@RequestParam("upload") ArrayList<MultipartFile> uploads,
                              HttpServletRequest request) throws Exception {
        for (MultipartFile upload : uploads) {
            System.out.println("文件名称:" + upload.getOriginalFilename());
            System.out.println("文件是否是js为后缀:" + upload.getOriginalFilename().endsWith(".js"));
            // 先获取到要上传的文件目录
            // String path = request.getSession().getServletContext().getRealPath("/uploads");
            String path = System.getProperty("user.dir") + "/uploads";
            System.out.println(path);
            // 创建File对象,一会向该路径下上传文件
            File file = new File(path);
            // 判断路径是否存在,如果不存在,创建该路径
            if (!file.exists()) {
                file.mkdirs();
            }
            // 获取到上传文件的名称
            String filename = upload.getOriginalFilename();
            String uuid = UUID.randomUUID().toString().replaceAll("-", "").toUpperCase();
            // 把文件的名称唯一化
            filename = uuid + "_" + filename;
            // 上传文件
            upload.transferTo(new File(file, filename));
            System.out.println(filename);
        }

        return "success";
    }
}

前端页面

<input type="file" name="upload" multiple/>

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>MultipartFile</title>
</head>
<body>
    <h3>文件上传</h3>
    <form th:action="@{/fileupload}" method="post" enctype="multipart/form-data">
        选择文件:<input type="file" name="upload" multiple/><br/>
        <input type="submit" value="上传文件"/>
    </form>
</body>
</html>

验证表单数据并实现数据的自定义认证

自定义注解类

先自定义注解,以便实体 Bean 中使用它

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


//用于指定使用范围,该处限定只能在字段上使用
@Target({ElementType.FIELD})
//表示注解在运行时可以通过反射获取到
@Retention(RetentionPolicy.RUNTIME)
//@Constraint注解,里面传入了一个validatedBy的字段,指定该注解校验逻辑
@Constraint(validatedBy = MyConstraintValidator.class)
public @interface MyConstraint {
    /**
     * @Description: 错误提示
     */
    String message() default "请输入中国政治或者经济中心的城市名";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

自定义验证业务逻辑类

initialize:初始化验证消息的方法

isValid:执行验证的方法

验证方法是用来验证业务逻辑的,他需要继承 ConstraintValidator 接口

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

public class MyConstraintValidator implements ConstraintValidator<MyConstraint, String> {
    //String为校验的类型
    @Override
    public void initialize(MyConstraint myConstraint) {
        //启动时执行
    }

    /**
     * @Description: 自定义校验逻辑
     */
    @Override
    public boolean isValid(String s, ConstraintValidatorContext validatorContext) {
        if (!(s.equals("北京") || s.equals("上海"))) {
            return false;
        }

        return true;
    }
}

创建实体

创建实体 Bean,用于表单验证。注意:定义的所有字段都需要被验证,否则会出错

import com.jia.config.MyConstraint;
import lombok.Data;
import org.hibernate.validator.constraints.Length;

import javax.validation.constraints.*;
import java.io.Serializable;

@Data
public class User implements Serializable {
    /**
     * 主键ID
     */
    private Long id;

    @NotBlank(message = "用户名不能为空")
    @Length(min = 5, max = 20, message = "用户名长度为5-20个字符")
    private String name;

    @NotNull(message = "年龄不能为空")
    @Min(value = 18 ,message = "最小18岁")
    @Max(value = 60,message = "最大60岁")
    private Integer age;

/*    @NotBlank(message = "电话不可以为空")
    @Length(min = 1, max = 13, message = "电话长度需要在13个字符以内")
    private String phone;*/

    @Email(message = "请输入邮箱")
    @NotBlank(message = "邮箱不能为空")
    private String email;

   /* @NotNull(message = "必须指定用户状态")
    @Min(value = 0, message = "用户状态不合法")
    @Max(value = 1, message = "用户状态不合法")
    private Integer status;*/

    @MyConstraint
    private String answer;

}

编写验证控制器

import com.jia.pojo.User;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import javax.validation.Valid;

@Controller
public class TestValidator {

    @GetMapping("/test")
    public String showForm(User user) {
        return "form";
    }

    @GetMapping("/results")
    public String results() {
        return "results";
    }

    @PostMapping("/test")
    public String checkUser(@Valid User user, BindingResult bindingResult, RedirectAttributes attr) {
        //特别注意实体中的属性必须都验证过了,不然不会成功
        if (bindingResult.hasErrors()) {
            return "form";
        }
        /**
         * @Description:
         * 1.使用RedirectAttributes的addAttribute方法传递参数会跟随在URL后面 ,如上代码即为?name=long&age=45
         * 2.使用addFlashAttribute不会跟随在URL后面,会把该参数值暂时保存于session,待重定向url获取该参数后从session中移除,
         * 这里的redirect必须是方法映射路径。你会发现redirect后的值只会出现一次,刷新后不会出现了,对于重复提交可以使用此来完成。
         */
        attr.addFlashAttribute("user", user);
        return "redirect:/results";

    }
}
 

addAttribute 方法:用 RedirectAttribute 的 attribute 方法传递参数,参数应跟在 URL 后面

addFlashAttribute 方法:参数不会跟在 URL 后面,会暂存在 session 中

redirect 方法:必须是方法的映射路径。redirect 后的值只会出现一次,刷新后不会出现。可以使用 redirect 方法来防止重复提交

编写视图

提交表单和返回验证

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<body>
<style type="text/css">
   .warn{color:red}
</style>
<form th:action="@{/test}" th:object="${user}" method="post">
    <div>
        <div>
            <span>姓名:</span>
            <span><input type="text" th:field="*{name}"/></span>
            <span  class="warn" th:if="${#fields.hasErrors('name')}" th:errors="*{name}">名字错误</span>
        </div>
        <div>
            <span>年龄:</span>
            <span><input type="number" th:field="*{age}"/></span>
            <span  class="warn" th:if="${#fields.hasErrors('age')}" th:errors="*{age}">年龄错误</span>
        </div>
        <div>
            <span>邮箱:</span>
            <span><input TYPE="text" th:field="*{email}"/></span>
            <span  class="warn" th:if="${#fields.hasErrors('email')}" th:errors="*{email}">邮箱错误</span>
        </div>
        <div>
            <span>验证答案:</span>
            <span><input TYPE="text" th:field="*{answer}"/></span>
            <span  class="warn" th:if="${#fields.hasErrors('answer')}" th:errors="*{answer}">答案错误</span>
        </div>
        <div>
            <span>
                <button type="submit">提交</button>
            </span>
        </div>
    </div>
</form>
</body>
</html>
 

编写验证通过后的处理视图

如果验证成功,则在本视图中渲染数据

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<body>
    <div th:each="user: ${user}">
        恭喜您,<span th:text="${user.name}">名字</span>(先生/女士),数据提交成功!
        <div>您的年龄是:
            <span th:text="${user.age}"></span></div>
        <div> 您的邮箱是:
            <span th:text="${user.email}"></span>
        </div>
    </div>
</body>
</html>

测试结果

处理公共代码块

用 fragment 标记重复代码块

可以通过 th:fragment="xxx" 标签来标记重复代码块,如:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>header</title>
</head>
<body>
    <div th:fragment="header" class="header" id="header">
        公共 header
    </div>
</body>
</html>
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>footer</title>
</head>
<body>
    <div th:fragment="footer" class="footer" id="footer">
        公共 footer
    </div>
</body>
</html>

调用重复代码块

<div th:replace="~{目录/公共资源(html)::xxx}"></div>
<div th:include="~{目录/公共资源(html)::xxx}"></div>

replace

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>使用 header 和 footer</title>
    <style>
        .header {
            color: red;
        }
        .footer {
            color: red;
        }
    </style>
</head>
<body>
    <h2>replace 的调用方式</h2>
    <div th:replace="~{header::header}"></div>
    <div>replace 正文</div>
    <div th:replace="~{footer::footer}"></div>
</body>
</html>

include 

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>使用 header 和 footer</title>
    <style>
        .header {
            color: red;
        }
        .footer {
            color: red;
        }
    </style>
</head>
<body>
    <h2>include 的调用方式</h2>
    <div th:include="~{header::header}"></div>
    <div>include 正文</div>
    <div th:include="~{footer::footer}"></div>
</body>
</html>

测试结果 

  • th:replace :替换当前标签为模板中的标签
    <div class="header" id="header">
        公共 header
    </div>
  • th:include : 只加载模板的内容
    <div>
        公共 header
    </div>

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值