FileLimit – AOP最佳实践

2022-10-31 18:43:39 浏览数 (1)

@FileLimit 结构分析

1、FileLimitUnit 定义枚举:文件的单位

代码语言:javascript复制
public enum FileLimitUnit {
    KB, MB, GB
}

2、定义注解。(如想了解定义注解的原注解么,请查看:https://cloud.tencent.com/developer/article/1935374)

代码语言:javascript复制
import org.springframework.core.annotation.AliasFor;
import java.lang.annotation.*;

/**
 * @Author :zanglk
 * @DateTime :2022/1/25 09:49
 * @Description :FileLimit 注解,内置参数value,max,以及文件单位
 * @Notes :To change this template:Click IDEA-Preferences to search 'File Templates'
 */
@Documented
@Target(ElementType.METHOD) // 作用与方法上
@Retention(RetentionPolicy.RUNTIME) // RUNTIME: 在运行时有效(即运行时保留)
public @interface FileLimit {

    @AliasFor("max") // @AliasFor 表示其可与max互换别名:当注解指定value时,为max赋值
    int value() default 5;

    // 定义单个文件最大限制
    @AliasFor("value") // @AliasFor 表示其可与value互换别名:当注解指定max是,为value赋值
    int max() default 5;

    // 文件单位,默认定义为MB
    FileLimitUnit unit() default FileLimitUnit.MB;
}

3、注解使用

代码语言:javascript复制
任意方法添加,如果不传入value,就使用注解默认值5。
    
    @FileLimit(value = 25,unit = FileLimitUnit.MB)

    @FileLimit(unit = FileLimitUnit.MB)

    @FileLimit

4、AOP拦截控制

注意 AOP拦截我在输出异常信息时,转换了文件大小,所以我使用我自己的工具类,这里我就不贴代码了,如果使用,请直接复制:https://cloud.tencent.com/developer/article/1936878

代码语言:javascript复制
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

@Aspect
@Component
public class FileLimitAop {

    // 定义默认的单个文件最大限制 5MB 。5Mb = 5 * 1024 * 1024 byte
    private static final long MAX_FILE_SIZE = 5 * 1024 * 1024;

    // 注意,这里要指定注解的全限定类名。不然无法进入AOP拦截自定义注解FileLimit
    @Pointcut("@annotation(com.zanglikun.springdataredisdemo.aop.fileLimitAop.FileLimit)")
    public void pointcut() {
    }

    /**
     * 方法体执行之前执行
     */
    @Before("pointcut()")
    public void beforeLog(JoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        FileLimit annotation = AnnotationUtils.getAnnotation(signature.getMethod(), FileLimit.class);
        if (null == annotation) {
            return;
        }
        // 执行文件检查
        fileSizeLimit(joinPoint, annotation);
    }

    // 判定文件大小是否合格,如果不合格,直接跑出自定义异常FileLimitException。进而阻塞方法正常进行。
    private void fileSizeLimit(JoinPoint joinPoint, FileLimit annotation) {
        // 获取AOP签名
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        // 获取注解的指定最大文件大小
        long maxFileSize = getAnnotationMaxFileSize(annotation);
        // 通过AOP签名 获取接口参数,调用方法获取文件
        MultipartFile multipartFile = getMultipartFile(signature.getParameterNames(), signature.getParameterTypes(), joinPoint.getArgs());
        if (null != multipartFile) {
            if (0 == multipartFile.getSize()) {
                throw new FileLimitException("文件数据(大小为0)异常.");
            }
            if (multipartFile.getSize() > maxFileSize) {
                String msg = "文件大小不得超过 "   annotation.max()   annotation.unit().toString();
                throw new FileLimitException(msg " ,当前文件大小是 " FileSizeOut.printFileSize(multipartFile.getSize()));
            }
        }
    }

    // 获取使用注解指定最大文件大小。如果没有指定文件大小,就用默认值
    public long getAnnotationMaxFileSize(FileLimit fileLimit) {
        if (null == fileLimit) {
            return MAX_FILE_SIZE;
        }
        switch (fileLimit.unit()) {
            case MB:
                return (long) fileLimit.max() * 1024 * 1024;
            case KB:
                return (long) fileLimit.max() * 1024;
            default:
                return MAX_FILE_SIZE;
        }
    }

    // 使用AOP获取目标请求方法携带的参数。我们当前只需要解析File
    private MultipartFile getMultipartFile(String[] paramNames, Class[] paramTypes, Object[] paramObjs) {
        for (int i = 0; i < paramNames.length; i  ) {
            if (paramTypes[i] != MultipartFile.class) {
                continue;
            }
            return (MultipartFile) paramObjs[i];
        }
        return null;
    }

}

测试

测试前,请添加或修改配置文件,避免因为Servlet大小限制了

代码语言:javascript复制
# 限制Tomcat Servlet单文件大小、以及总请求大小,-1则不限制!
spring.servlet.multipart.max-file-size=-1
spring.servlet.multipart.max-request-size=-1
代码语言:javascript复制
@RestController
@RequestMapping("/testFileLimit")
public class TestFileLimit {

    @RequestMapping("/upload")
    //@FileLimit(value = 25,unit = FileLimitUnit.MB)
    //@FileLimit(unit = FileLimitUnit.MB)
    @FileLimit
    public void upLoad(MultipartFile file) {
        System.out.println("请求进入喽");
    }
}

我上传一个视频文件461.0MB

代码语言:javascript复制
2022-10-25 12:55:56.245 ERROR 42691 --- [nio-8081-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is com.zanglikun.springdataredisdemo.aop.FileLimitException: 文件大小不得超过 5MB ,当前文件大小是 461.0MB] with root cause

com.zanglikun.springdataredisdemo.aop.FileLimitException: 文件大小不得超过 5MB ,当前文件大小是 461.0MB

拦截成功!完结

0 人点赞