文件上传

原理

  • 表单的enctype属性规定在发送到服务器之前应该如何对表单数据进行编码。
  • 当表单的enctype="application/x-www-form-urlencoded"(默认)时,form表单中的数据格式为:key=value&key=value
  • 当表单的enctype="multipart/form-data"时,其传输数据形式如下
    image-20220902104613256

SpringBoot实现文件上传功能

  • SpringBoot工程嵌入的tomcat限制了请求的文件大小,每个文件的配置最大为1Mb,单次请求的文件总数不能大于10Mb(这些限制是可修改的)
  • 要更改这个默认值需要在配置文件(如application.properties)中加入两个配置

    spring.servlet.multipart.max-file-size=10MB
    spring.servlet.multipart.max-request-size=10MB
  • 当表单的enctype="multipart/form-data"时,可以使用MultipartFile获取上传的文件数据,再通过transferTo方法将其写入到磁盘中

    package com.example.demo.controller;
    
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.multipart.MultipartFile;
    
    import javax.servlet.http.HttpServletRequest;
    import java.io.File;
    import java.io.IOException;
    
    @RestController
    public class FileController {
    
        /**
         * 该接口要求用户POST的时候携带两个参数,即用户名nickname和文件f
         * @param nickname
         * @param f
         * @return success
         */
        @PostMapping("upload")
        public String upload(String nickname, MultipartFile f, HttpServletRequest request) throws IOException{
            System.out.println("上传者:" + nickname);
            System.out.println("文件大小:" + f.getSize());
            System.out.println("文件类型:" + f.getContentType());
            System.out.println("原文件名:" + f.getOriginalFilename());
            // request.getServletContext().getRealPath()方法获得一个tomcat服务器内的临时文件夹
            // 再给getRealPath方法传入一个参数以在临时文件夹中创建一个新的文件夹
            String path = request.getServletContext().getRealPath("/upload/");
            System.out.println("存放路径:" + path);
            saveFile(f,path);
            return "上传成功";
        }
    
        // 响应上传文件后的处理方法
        public void saveFile(MultipartFile f, String path) throws IOException{
            // 定义上传文件夹
            File dir = new File(path);
            // 判断上传文件夹是否存在,不存在则自动创建
            if(!dir.exists()){
                dir.mkdir();
            }
            // 上传后的路径与文件名
            File file = new File(path + f.getOriginalFilename());
            // 进行保存
            f.transferTo(file);
        }
    }
    

文件上传之后,会默认放在tomcat服务器的临时文件夹下的路径,这个路径是随时变动的

通过这种方式上传的文件无法通过浏览器请求进行访问,如果需要浏览器请求访问,则需要修改配置文件

为上传的文件配置一个静态目录

spring.web.resources.static-locations=/upload/

因为这是个tomcat服务器下的临时路径,这个路径是一个系统上的路径,并不位于classpath下。故直接使用/路径即可

image-20220903101348269可以看到成功访问到了上传的图片

拦截器

拦截器在Web系统中非常常见,对于某些全局统一的操作,我们可以把它提取到拦截器中实现。总结起来,拦截器大致有以下几种使用场景:

  • 权限检查:如登录检测、进入处理程序检测是否登录,如果没有,则直接返回登录页面。
  • 性能监控:有时系统在某段时间莫名其妙很慢,可以通过拦截器在进入处理程序之前开始记录时间,在处理完后记录结束时间,从而得到该请求的处理时间
  • 通用行为:读取cookie得到用户信息并将用户对象放入请求,从而方便后续流程使用,还有提取Locale、Theme信息等,只要是多个处理程序都需要得,即可使用拦截器实现

SpringBoot定义了HandlerInterceptor接口来实现自定义拦截器的功能

HandlerInterceptor接口定义了preHandle、postHandle、afterCompletion三种方法,通过重写这三种方法实现请求前、后等操作

拦截器

编写拦截器

在主包下创建一个包名为interceptor来存放相关的拦截器类

这里创建一个LoginInterceptor类来进行演示

package com.example.demo.interceptor;

import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

// 拦截器需要实现HandlerInterceptor这个接口
public class LoginInterceptor implements HandlerInterceptor {
    /**
     *
     * @param request HTTP请求
     * @param response HTTP响应
     * @param handler chosen handler to execute, for type and/or instance evaluation
     * @return boolean
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        System.out.println("LoginInterceptor");
        return true;
    }
}

拦截器返回的是一个布尔值,只有该值为真时,才会将请求转交给下一个拦截器或者控制器

创建拦截器后,还需要进行注册才能生效

拦截器注册

写完拦截器后还需要在类中进行注册

  • addPathPatterns方法定义拦截的地址
  • excludePathPatterns定义排除某些地址不被拦截
  • 添加的一个拦截器没有addPathPattern任何一个url则默认拦截所有请求
  • 如果没有excludePathPatterns任何一个请求,则默认不放过任何一个请求

在主包下创建一个包名为config用于存放配置文件

新建一个WebConfig类来注册刚刚创建的拦截器

package com.example.demo.config;

import com.example.demo.interceptor.LoginInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

// 需要加上Configuration注解让SpringBoot读取这个类,这样下面的配置才能够生效
@Configuration
public class WebConfig implements WebMvcConfigurer {
    /**
     * 重写WebMvcConfigurer接口中增加拦截器的方法
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry){
        /*
            调用registry对象添加拦截器的方法,传入我们设置的拦截器,
            并调用addPathPatterns指定拦截的路径
            如果不指定拦截的路径,那么所有的资源都会被这个拦截器拦截处理
         */
        registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/user/**");
    }
}

前面定义的拦截器中,对拦截到的路径的处理是打印LoginInterceptor

可以通过访问这个路径来看是否成功拦截到了请求

拦截器的注册类中定义的是拦截/user下的所有请求,尝试着访问

image-20220903105329671

可以看到控制台成功打印出了LoginInterceptor,说明拦截器生效了

如果访问其他路径,控制台则不会打印,拦截器不生效

image-20220903105456182

最后修改:2022 年 09 月 05 日
如果觉得我的文章对你有用,能不能v我50参加疯狂星期四