spring boot通过Constraint和ConstraintValidator自定义参数校验注解 文件上传前置校验注解 validMessage

发布于:2024-06-08 ⋅ 阅读:(117) ⋅ 点赞:(0)

Constraint注解

在spring boot进行开发的时候,有时需要对请求的参数进行校验, 而@Constraint可以实现自定义的校验注解。

Spring boot自定义参数校验注解

1. 如果Spring boot是2.3.0之后的版本,需要引入新的依赖
		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>

2. 自定义注解
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = MultipartFileValidator.class)
public @interface MultipartFileUploadPreCheck {

    String DEFAULT_MAXSIZE = "-1";

    /**
     * AliasFor("endsWith")
     */
//    FileUtils.Type[] value() default {};

    /**
     * 支持的文件后缀类型,默认全部,AliasFor("value")
     */
    FileUtils.Type[] allowFileType() default {};

    /**
     * 文件后缀是否区分大小写
     */
    boolean ignoreCase() default true;

    /**
     * 上传的文件是否允许为空
     */
    boolean allowEmpty() default false;

    /**
     * Max file size. Values can use the suffixes "MB" or "KB" to indicate megabytes or
     * kilobytes respectively.<br/>
     * 默认不限制但必须小于等于SpringMVC中文件上传配置
     */
    String maxSize() default DEFAULT_MAXSIZE;

    /**
     * Min file size. Values can use the suffixes "MB" or "KB" to indicate megabytes or
     * kilobytes respectively. default byte
     */
    String minSize() default "0MB";

    String message() default "The uploaded file is not verified.";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

}

3. FileUtils.Type文件
    @RequiredArgsConstructor(access = AccessLevel.PROTECTED)
    public static enum Type {

        /**
         * json
         */
        JSON("json"),
        /**
         * JEPG.
         */
        JPEG("FFD8FF"),

        JPG("FFD8FF"),

        /**
         * PNG.
         */
        PNG("89504E47"),

        /**
         * GIF.
         */
        GIF("47494638"),

        /**
         * TIFF.
         */
        TIFF("49492A00"),

        /**
         * Windows Bitmap.
         */
        BMP("424D"),

        /**
         * CAD.
         */
        DWG("41433130"),

        /**
         * Adobe Photoshop.
         */
        PSD("38425053"),

        /**
         * Rich Text Format.
         */
        RTF("7B5C727466"),

        /**
         * XML.
         */
        XML("3C3F786D6C"),

        /**
         * HTML.
         */
        HTML("68746D6C3E"),
        /**
         * CSS.
         */
        CSS("48544D4C207B0D0A0942"),
        /**
         * JS.
         */
        JS("696B2E71623D696B2E71"),
        /**
         * Email [thorough only].
         */
        EML("44656C69766572792D646174653A"),

        /**
         * Outlook Express.
         */
        DBX("CFAD12FEC5FD746F"),

        /**
         * Outlook (pst).
         */
        PST("2142444E"),

        /**
         * MS Word/Excel.
         * XLS_DOC:ppt,doc,xls
         * XLSX_DOCX:xlsx
         */
        XLS("D0CF11E0"), XLSX("504B0304"),

        DOC("D0CF11E0"), DOCX("504B0304"),
        /**
         * Visio
         */
        VSD("d0cf11e0a1b11ae10000"),
        /**
         * MS Access.
         */
        MDB("5374616E64617264204A"),
        /**
         * WPS文字wps、表格et、演示dps都是一样的
         */
        WPS("d0cf11e0a1b11ae10000"),
        /**
         * torrent
         */
        TORRENT("6431303A637265617465"),
        /**
         * WordPerfect.
         */
        WPD("FF575043"),

        /**
         * Postscript.
         */
        EPS("252150532D41646F6265"),

        /**
         * Adobe Acrobat.
         */
        PDF("255044462D312E"),

        /**
         * Quicken.
         */
        QDF("AC9EBD8F"),

        /**
         * Windows Password.
         */
        PWL("E3828596"),

        /**
         * ZIP Archive.
         */
//        ZIP("504b0304140000000800"),

        /**
         * RAR Archive.
         */
        RAR("52617221"),
        /**
         * JSP Archive.
         */
        JSP("3C2540207061676520"),
        /**
         * JAVA Archive.
         */
        JAVA("7061636B61676520"),
        /**
         * CLASS Archive.
         */
        CLASS("CAFEBABE0000002E00"),
//        /**
//         * JAR Archive.
//         */
//        JAR("504b03040a0000000000"),
        /**
         * MF Archive.
         */
        MF("4D616E69666573742D56"),
        /**
         * EXE Archive.
         */
        EXE("4D5A9000030000000400"),
        /**
         * CHM Archive.
         */
        CHM("49545346030000006000"),
        /**
         * Wave.
         */
        WAV("57415645"),

        /**
         * AVI.
         */
        AVI("41564920"),

        /**
         * Real Audio.
         */
        RAM("2E7261FD"),

        /**
         * Real Media.
         */
        RM("2E524D46"),

        /**
         * MPEG (mpg).
         */
        MPG("000001BA"),

        /**
         * Quicktime.
         */
        MOV("6D6F6F76"),

        /**
         * Windows Media.
         */
        ASF("3026B2758E66CF11"),

        /**
         * MIDI.
         */
        MID("4D546864"),
        /**
         * MP4.
         */
        MP4("00000020667479706d70"),
        /**
         * MP3.
         */
        MP3("49443303000000002176"),
        /**
         * FLV.
         */
        FLV("464C5601050000000900"),
        /**
         * TXT:txt,docx
         */
        TXT("0000000000000000000000000000");
        @Getter
        private final String value;
        private static HashMap<String, Type> codeValueMap = new HashMap<>(47);

        static {

            for (FileUtils.Type currentOne : FileUtils.Type.values()) {
                codeValueMap.put(currentOne.getValue(), currentOne);
            }
        }

        public static FileUtils.Type getInstance(int code) {
            return codeValueMap.get(code);
        }

        public static boolean exists(int code) {
            return codeValueMap.containsKey(code);
        }
    }

3. 编写自定义注解处理文件
public class MultipartFileValidator implements ConstraintValidator<MultipartFileUploadPreCheck, MultipartFile> {

    @Autowired
    private MultipartProperties multipartProperties;

    private long maxSize = -1;
    private long minSize = 0;

    private MultipartFileUploadPreCheck multipartFileUploadPreCheck;
    private final ArrayList<FileUtils.Type> extension = new ArrayList<>();

    @Override
    public void initialize(MultipartFileUploadPreCheck constraintAnnotation) {
        this.multipartFileUploadPreCheck = constraintAnnotation;
        //支持的文件扩展名集合
//        Collections.addAll(extension, multipartFileUploadPreCheck.value());
        Collections.addAll(extension, multipartFileUploadPreCheck.allowFileType());
        //文件上传的最大值
        if (constraintAnnotation.maxSize().equals(MultipartFileUploadPreCheck.DEFAULT_MAXSIZE)) {
            //默认最大值采用Spring中配置的单文件大小
            DataSize maxFileSize = multipartProperties.getMaxFileSize();
            this.maxSize = parseSize(maxFileSize.toString());
        } else {
            this.maxSize = parseSize(constraintAnnotation.maxSize());
        }
        //文件上传的最小值
        this.minSize = parseSize(constraintAnnotation.minSize());
    }

    private long parseSize(String size) {
        Assert.hasLength(size, "Size must not be empty");
        size = size.toUpperCase();
        long length = Long.parseLong(size.substring(0, size.length() - 2));
        if (size.endsWith("KB")) {
            return length * 1024;
        }
        if (size.endsWith("MB")) {
            return length * 1024 * 1024;
        }
        return Long.parseLong(size);
    }

    /**
     * 多个文件也可以 效验
     * @param multipartFiles
     * @param cvc
     * @return
     */
    @SneakyThrows
    public boolean isValids(MultipartFile[] multipartFiles, ConstraintValidatorContext cvc) {
        //上传的文件是空的情况
        if (Objects.isNull(multipartFiles)) {
            if (multipartFileUploadPreCheck.allowEmpty()) {
                return true;
            }
            validMessage("上传文件不能为空", cvc);
            return false;
        }
        for (MultipartFile multipartFile : multipartFiles) {
            boolean valid = isValid(multipartFile, cvc);
            if (!valid) {
                return false;
            }
        }
        return true;
    }

    @SneakyThrows
    @Override
    public boolean isValid(MultipartFile multipartFile, ConstraintValidatorContext cvc) {
        String fieldName = multipartFile.getName();
        //上传的文件是空的情况
        if (multipartFile.isEmpty()) {
            if (multipartFileUploadPreCheck.allowEmpty()) {
                return true;
            }
            validMessage("上传文件不能为空" + ",参数名:" + fieldName, cvc);
            return false;
        }
        //上传的文件不是空的情况,验证其他条件是否成立
        //获取文件名,如果上传文件后缀名不区分大小写则统一转成小写
        String originalFilename = multipartFile.getOriginalFilename();
        if (multipartFileUploadPreCheck.ignoreCase()) {
            originalFilename = originalFilename.toLowerCase();
        }

        if (StringUtils.isBlank(originalFilename)){
            validMessage("上传文件名不能为空", cvc);
            return false;
        }

        // 先通过后缀名过滤一部分明显错误的, 因为读取文件流获取文件头信息 比较消耗资源
//        String fileSuffixName = originalFilename.substring(originalFilename.lastIndexOf('.') + 1);
//        List<String> allowedFileSuffixNames = extension.stream().map(e -> e.name().toLowerCase(Locale.ROOT)).collect(Collectors.toList());
//        if (StringUtils.isNotBlank(fileSuffixName) && allowedFileSuffixNames.stream().noneMatch(e -> e.equals(fileSuffixName))) {
//            validMessage("上传文件类型不符合要求" + ",参数名:" + fieldName, cvc);
//            return false;
//        }

        FileUtils.Type type = FileUtils.getType(multipartFile.getInputStream());

        if (extension.size() > 0 && extension.stream().noneMatch(e -> e.equals(type))) {
            validMessage("上传文件类型不符合要求" + ",参数名:" + fieldName, cvc);
            return false;
        }
        //上传文件字节数
        long size = multipartFile.getSize();
        if (size < this.minSize) {
            validMessage("上传文件不能小于指定最小值" + ",参数名:" + fieldName, cvc);
            return false;
        }
        if (size > this.maxSize) {
            validMessage("上传文件不能大于指定最大值" + ",参数名:" + fieldName, cvc);
            return false;
        }
        return true;
    }

    private void validMessage(String message, ConstraintValidatorContext cvc) {
        cvc.disableDefaultConstraintViolation();
        cvc.buildConstraintViolationWithTemplate(message)
                .addConstraintViolation();
    }

}

网站公告

今日签到

点亮在社区的每一天
去签到