SpringBoot实现文件上传功能详解

利用SpirngBoot实现文件上传功能

零、本篇要点

  • 介绍SpringBoot对文件上传的自动配置。
  • 介绍MultipartFile接口。
  • 介绍SpringBoot+Thymeleaf文件上传demo的整合。
  • 介绍对文件类型,文件名长度等判断方法。

一、SpringBoot对文件处理相关自动配置

自动配置是SpringBoot为我们提供的便利之一,开发者可以在不作任何配置的情况下,使用SpringBoot提供的默认设置,如处理文件需要的MultipartResolver。

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Servlet.class,StandardServletMultipartResolver.class,MultipartConfigElement.class })
@ConditionalOnProperty(prefix = "spring.servlet.multipart",name = "enabled",matchIfMissing = true)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(MultipartProperties.class)
public class MultipartAutoConfiguration {

	private final MultipartProperties multipartProperties;

	public MultipartAutoConfiguration(MultipartProperties multipartProperties) {
		this.multipartProperties = multipartProperties;
	}

	@Bean
	@ConditionalOnMissingBean({ MultipartConfigElement.class,CommonsMultipartResolver.class })
	public MultipartConfigElement multipartConfigElement() {
		return this.multipartProperties.createMultipartConfig();
	}

	@Bean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
	@ConditionalOnMissingBean(MultipartResolver.class)
	public StandardServletMultipartResolver multipartResolver() {
		StandardServletMultipartResolver multipartResolver = new StandardServletMultipartResolver();
		multipartResolver.setResolveLazily(this.multipartProperties.isResolveLazily());
		return multipartResolver;
	}

}
  • Spring3.1之后支持StandardServletMultipartResolver,且默认使用StandardServletMultipartResolver,它的优点在于:使用Servlet所提供的功能支持,不需要依赖任何其他的项目。
  • 想要自动配置生效,需要配置spring.servlet.multipart.enabled=true,当然这个配置默认就是true。
  • 相关的配置设置在MultipartProperties中,其中字段就是对应的属性设置,经典字段有:
    • enabled:是否开启文件上传自动配置,默认开启。
    • location:上传文件的临时目录。
    • maxFileSize:最大文件大小,以字节为单位,默认为1M。
    • maxRequestSize:整个请求的最大容量,默认为10M。
    • fileSizeThreshold:文件大小达到该阈值,将写入临时目录,默认为0,即所有文件都会直接写入磁盘临时文件中。
    • resolveLazily:是否惰性处理请求,默认为false。
  • 我们也可以自定义处理的细节,需要实现MultipartResolver接口。

二、处理上传文件MultipartFile接口

SpringBoot为我们提供了MultipartFile强大接口,让我们能够获取上传文件的详细信息,如原始文件名,内容类型等等,接口内容如下:

public interface MultipartFile extends InputStreamSource {
    String getName(); //获取参数名
    @Nullable
    String getOriginalFilename();//原始的文件名
    @Nullable
    String getContentType();//内容类型
    boolean isEmpty();
    long getSize(); //大小
    byte[] getBytes() throws IOException;// 获取字节数组
    InputStream getInputStream() throws IOException;//以流方式进行读取
    default Resource getResource() {
        return new MultipartFileResource(this);
    }
    // 将上传的文件写入文件系统
    void transferTo(File var1) throws IOException,IllegalStateException;
	// 写入指定path
    default void transferTo(Path dest) throws IOException,IllegalStateException {
        FileCopyUtils.copy(this.getInputStream(),Files.newOutputStream(dest));
    }
}

三、SpringBoot+Thymeleaf整合demo

1、编写控制器

/**
 * 文件上传
 *
 * @author Summerday
 */
@Controller
public class FileUploadController {

    private static final String UPLOADED_FOLDER = System.getProperty("user.dir");

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

    @PostMapping("/upload")
    public String singleFileUpload(@RequestParam("file") MultipartFile file,RedirectAttributes redirectAttributes) throws IOException {

        if (file.isEmpty()) {
            redirectAttributes.addFlashAttribute("msg","文件为空,请选择你的文件上传");
            return "redirect:uploadStatus";

        }
        saveFile(file);
        redirectAttributes.addFlashAttribute("msg","上传文件" + file.getOriginalFilename() + "成功");
        redirectAttributes.addFlashAttribute("url","/upload/" + file.getOriginalFilename());
        return "redirect:uploadStatus";
    }

    private void saveFile(MultipartFile file) throws IOException {
        Path path = Paths.get(UPLOADED_FOLDER + "/" + file.getOriginalFilename());
        file.transferTo(path);
    }

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

2、编写页面file.html

<html xmlns:th="http://www.thymeleaf.org">
    <!--suppress ALL-->
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>文件上传界面</title>
    </head>
    <body>
        <div>
            <form method="POST" enctype="multipart/form-data" action="/upload">
                <table>
                    <tr><td><input type="file" name="file" /></td></tr>
                    <tr><td></td><td><input type="submit" value="上传" /></td></tr>
                </table>
            </form>

        </div>
    </body>
</html>

3、编写页面uploadStatus.html

<!--suppress ALL-->
<html xmlns:th="http://www.thymeleaf.org">

<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>文件上传界面</title>
    </head>
    <body>
        <div th:if="${msg}">
            <h2 th:text="${msg}"/>
        </div>
        <div >
            <img src="" th:src="${url}" alt="">
        </div>
    </body>
</html>

4、编写配置

server.port=8081
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=10MB

5、配置虚拟路径映射

这一步是非常重要的,我们将文件上传到服务器上时,我们需要将我们的请求路径和服务器上的路径进行对应,不然很有可能文件上传成功,但访问失败:

@Configuration
public class MvcConfig implements WebMvcConfigurer {

    private static final String UPLOADED_FOLDER = System.getProperty("user.dir");

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/upload/**")
                .addResourceLocations("file:///" + UPLOADED_FOLDER + "/");
    }
}

对应关系需要自己去定义,如果访问失败,可以试着打印以下路径,看看是否缺失了路径分隔符。

注意:如果addResourceHandler不要写成处理/**,这样会拦截掉其他的请求

6、测试页面

执行mvn spring-boot:run,启动程序,访问http://localhost:8081/,选择文件,点击上传按钮,我们的项目目录下出现了mongo.jpg,并且页面也成功显示:

四、SpringBoot的Restful风格,返回url

/**
 * 文件上传
 *
 * @author Summerday
 */
@RestController
public class FileUploadRestController {

    /**
     * 文件名长度
     */
    private static final int DEFAULT_FILE_NAME_LENGTH = 100;

    /**
     * 允许的文件类型
     */
    private static final String[] ALLOWED_EXTENSIONS = {
            "jpg","img","png","gif"
    };

    /**
     * 项目路径
     */
    private static final String UPLOADED_FOLDER = System.getProperty("user.dir");

    @PostMapping("/restUpload")
    public Map<String,Object> singleFileUpload(@RequestParam("file") MultipartFile file) throws Exception {

        if (file.isEmpty()) {
            throw new Exception("文件为空!");
        }
        String filename = upload(file);
        String url = "/upload/" + filename;
        Map<String,Object> map = new HashMap<>(2);
        map.put("msg","上传成功");
        map.put("url",url);
        return map;
    }


    /**
     * 上传方法
     */
    private String upload(MultipartFile file) throws Exception {
        int len = file.getOriginalFilename().length();
        if (len > DEFAULT_FILE_NAME_LENGTH) {
            throw new Exception("文件名超出限制!");
        }
        String extension = getExtension(file);
        if(!isValidExtension(extension)){
            throw new Exception("文件格式不正确");
        }
        // 自定义文件名
        String filename = getPathName(file);
        // 获取file对象
        File desc = getFile(filename);
        // 写入file
        file.transferTo(desc);
        return filename;
    }

    /**
     * 获取file对象
     */
    private File getFile(String filename) throws IOException {
        File file = new File(UPLOADED_FOLDER + "/" + filename);
        if(!file.getParentFile().exists()){
            file.getParentFile().mkdirs();
        }
        if(!file.exists()){
            file.createNewFile();
        }
        return file;
    }

    /**
     * 验证文件类型是否正确
     */
    private boolean isValidExtension(String extension) {
        for (String allowedExtension : ALLOWED_EXTENSIONS) {
            if(extension.equalsIgnoreCase(allowedExtension)){
                return true;
            }
        }
        return false;
    }

    /**
     * 此处自定义文件名,uuid + extension
     */
    private String getPathName(MultipartFile file) {
        String extension = getExtension(file);
        return UUID.randomUUID().toString() + "." + extension;
    }

    /**
     * 获取扩展名
     */
    private String getExtension(MultipartFile file) {
        String originalFilename = file.getOriginalFilename();
        return originalFilename.substring(originalFilename.lastIndexOf('.') + 1);
    }
}

五、源码下载

本文内容均为对优秀博客及官方文档总结而得,原文地址均已在文中参考阅读处标注。最后,文中的代码样例已经全部上传至Gitee:https://gitee.com/tqbx/springboot-samples-learn,另有其他SpringBoot的整合哦。

六、参考阅读

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


今天小编给大家分享的是Springboot下使用Redis管道(pipeline)进行批量操作的介绍,相信很多人都不太了解,为了让大家更加了解,所以给大家总结了以下内容,一起...
本篇文章和大家了解一下springBoot项目常用目录有哪些。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。springBoot项目常用目录springBoot项...
本篇文章和大家了解一下Springboot自带线程池怎么实现。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。一: ThreadPoolTaskExecuto1 ThreadP...
这篇文章主要介绍了SpringBoot读取yml文件有哪几种方式,具有一定借鉴价值,需要的朋友可以参考下。下面就和我一起来看看吧。Spring Boot读取yml文件的主要方式...
今天小编给大家分享的是SpringBoot配置Controller实现Web请求处理的方法,相信很多人都不太了解,为了让大家更加了解,所以给大家总结了以下内容,一起往下看吧...
本篇文章和大家了解一下SpringBoot实现PDF添加水印的方法。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。简介PDF(Portable Document Form...
本篇文章和大家了解一下解决Springboot全局异常处理与AOP日志处理中@AfterThrowing失效问题的方法。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有...
本篇文章和大家了解一下IDEA创建SpringBoot父子Module项目的实现方法。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。目录前言1. 软硬件环...
今天小编给大家分享的是springboot获取项目目录路径的方法,相信很多人都不太了解,为了让大家更加了解,所以给大家总结了以下内容,一起往下看吧。一定会有所收...
本篇内容主要讲解“SpringBoot+Spring Security无法实现跨域如何解决”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面...
这篇文章主要介绍“vue怎么发送请求到springboot程序”,在日常操作中,相信很多人在vue怎么发送请求到springboot程序问题上存在疑惑,小编查阅了各式资料,整理...
本篇内容主要讲解“Springboot内置的工具类CollectionUtils如何使用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家...
本文小编为大家详细介绍“SpringBoot上传文件大小受限如何解决”,内容详细,步骤清晰,细节处理妥当,希望这篇“SpringBoot上传文件大小受限如何解决”文章能帮...
本文小编为大家详细介绍“springboot拦截器如何创建”,内容详细,步骤清晰,细节处理妥当,希望这篇“springboot拦截器如何创建”文章能帮助大家解决疑惑,下面...
本文小编为大家详细介绍“Hikari连接池使用SpringBoot配置JMX监控的方法是什么”,内容详细,步骤清晰,细节处理妥当,希望这篇“Hikari连接池使用SpringBoot配...
今天小编给大家分享一下SpringBoot如何使用Sa-Token实现权限认证的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大...
这篇文章主要介绍“SpringBoot如何集成SFTP客户端实现文件上传下载”,在日常操作中,相信很多人在SpringBoot如何集成SFTP客户端实现文件上传下...
本篇内容主要讲解“Springboot插件怎么开发”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Springboot插件怎
这篇文章主要介绍“Springboot怎么解决跨域请求问题”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇...
今天小编给大家分享一下如何在SpringBoot2中整合Filter的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文...