SpringBoot实现文件上传①


前言:在web开发中,文件上传是必不可少的一部分。比如头像上传,添加商品、上传图片等等需求......就是把文件上传保存到服务器的过程。

实现步骤

01、搭建项目

搭建一个SpringBoot项目

<?xml version="1.0" encoding="UTF-8"?>

    4.0.0
    
        org.springframework.boot
        spring-boot-starter-parent
        2.6.0
         
    
    com.qd
    uploadDemo
    0.0.1-SNAPSHOT
    uploadDemo
    文件上传demo
    
        1.8
    
    
        
            org.springframework.boot
            spring-boot-starter-freemarker
        
        
            org.springframework.boot
            spring-boot-starter-web
        

        
            org.springframework.boot
            spring-boot-starter-test
            test
        
    

    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
        
    

server:
  port: 8082 # 应用端口

spring:
  # freemarker
  freemarker:
    suffix: .html
    cache: false
  # 文件上传配置
  servlet:
    multipart:
      # 是否开启http上传处理
      enabled: true
      # 单个文件最大长度
      max-file-size: 2MB
      # 最大请求文件的大小
      max-request-size: 10MB
      # 设置临时目录
      # location: F://data//temp

02、准备文件上传页面

resources/templates/新增upload.html页面

package com.qd.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

/**
 * Create by IntelliJ IDEA
 *
 * @Author: qiandu
 * @Blog: https://www.cnblogs.com/qd666
 * @Date: 2021/11/21 21:01
 */
@Controller
public class UploadController {

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

03、后台实现

UploadService

package com.qd.service;

import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;

/**
 * 文件上传
 *
 * @Author: qiandu
 * @Blog: https://www.cnblogs.com/qd666
 * @Date: 2021/11/21 21:20
 */
@Service
public class UploadService {

    /**
     * MultipartFile 对象是springMVC提供的文件上传接收的类
     * 文件上传底层原理 request.getInpuStream()
     *
     * @param multipartFile
     * @param dir
     * @return
     */
    public String uploadImg(MultipartFile multipartFile, String dir) {

        // 1:指定文件上传的目录
        File targetFile = new File("D:\\tmp\\" + dir);

        try {
            // 2:如果targetFile不存在,则创建
            if (!targetFile.exists()) targetFile.mkdirs();
            // 3: 指定文件上传后的目录
            File targetFileName = new File(targetFile, "1.png"); // 先写死
            // 4:文件上传到指定的目录
            multipartFile.transferTo(targetFileName);
            return "ok";
        } catch (IOException e) {
            e.printStackTrace();
            return "fail";
        }
    }
}

UploadController

package com.qd.controller;

import com.qd.service.UploadService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
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;

/**
 * Create by IntelliJ IDEA
 *
 * @Author: qiandu
 * @Blog: https://www.cnblogs.com/qd666
 * @Date: 2021/11/21 21:01
 */
@Controller
public class UploadController {

    @Autowired
    private UploadService uploadService;


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

    @PostMapping("/upload/file")
    @ResponseBody
    public String upload(@RequestParam("file") MultipartFile multipartFile, HttpServletRequest request) {

        // 1:空判断
        if (multipartFile.isEmpty()) {
            return "文件为空!!!";
        }

        multipartFile.getSize();//得到大小
        multipartFile.getOriginalFilename();//得到文件名
        String contentType = multipartFile.getContentType();//得到文件类型

        // 2:判断文件是否符合
        if (!"image/png".equals(contentType) || !"image/jpg".equals(contentType)) {
            return "文件格式不符合";
        }

        // 3:获取用户指定的文件夹
        // 目的:作隔离,不同业务、不同文件放入到不同的目录中
        String dir = request.getParameter("dir");
        return uploadService.uploadImg(multipartFile, dir);
    }
}

web页面

    

文件上传

结果
在这里插入图片描述

04、后台改进

上面的还有问题的,比如上传后的文件名称被写死,目录页应该按照年月日层级划分

package com.qd.service;

import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;

/**
 * 文件上传
 *
 * @Author: qiandu
 * @Blog: https://www.cnblogs.com/qd666
 * @Date: 2021/11/21 21:20
 */
@Service
public class UploadService {

    /**
     * MultipartFile 对象是springMVC提供的文件上传接收的类
     * 文件上传底层原理 request.getInpuStream()
     *
     * @param multipartFile
     * @param dir
     * @return
     */
    public String uploadImg(MultipartFile multipartFile, String dir) {

        try {
            // 1:真实的文件名称
            String originalFilename = multipartFile.getOriginalFilename(); // 上传的文件aa.jpg
            // 2:截取后的文件名称 .jpg
            String imgSuffix = originalFilename.substring(originalFilename.lastIndexOf(".")); // 得到.jpg
            // 3:生成唯一的文件名称
            String newFileName = UUID.randomUUID().toString() + imgSuffix;  // 随机生成如:dfasf42432.jpg
            // 4:日期作为目录隔离文件
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd");
            String datePath = dateFormat.format(new Date()); // 日期目录:2021/11/21
            // 5:最终文件的上传目录
            File targetFile = new File("D:\\tmp\\" + dir, datePath); // 生成的最终目录; D://tmp/avatar/2021/11/21
            // 6:如果dirFile不存在,则创建
            if (!targetFile.exists()) targetFile.mkdirs();
            // 7: 指定文件上传后完整的文件名
            File dirFileName = new File(targetFile, newFileName); // 文件在服务器的最终路径是:D://tmp/avatar/2021/11/21/dfasf42432.jpg
            // 8:文件上传
            multipartFile.transferTo(dirFileName);
            return "ok";
        } catch (IOException e) {
            e.printStackTrace();
            return "fail";
        }
    }
}

结果
在这里插入图片描述

05、前台改进

目前,前端需要先选择上传的图片,然后需要点击上传按钮,方能上传。那能不能改进才只要选中图片即可自动上传呢?

    

文件上传

ok~到这里本地的单文件上传就学习的差不多了,随之我们又遇到了另外一个问题:怎么获取上传的图片呢? 那么接下来将解决这个问题。

获取文件

文件可访问路径:http://localhost:8082/avatar/2021/11/21/dfasf42432.jpg

	// 9:可访问的路径 http://localhost:8082/avatar/2021/11/21/dfasf42432.jpg
	String fileName = dir + "/" + datePath + "/" + newFileName;
	return fileName;

在这里插入图片描述
但是此时仍然无法访问,怎么解决呢?———上传至服务器目录 即当前tomcat服务器下webapps/目录

SpringBoot如何指定任意目录为资源的访问目录呢?

我们知道SpringBoot有一个目录static 在这个目录下的文件可以是直接通过http请求访问到的,但是程序会被打成jar包,文件无法写入,所以SpringBoot提供一个资源目录映射的机制。接下来我们尝试一下。

01、新建配置类

package com.qd.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * 静态资源映射配置类
 *
 * @Author: qiandu
 * @Blog: https://www.cnblogs.com/qd666
 * @Date: 2021/11/22 5:50
 */
@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {

    // 配置文件上传的额外的静态资源配置
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        //  registry.addResourceHandler("/资源的访问路径").addResourceLocations("映射目录");
        registry.addResourceHandler("/uploadImg/**").addResourceLocations("file:D://tmp//");
    }
}

分析:如果你将文件上传到了D://tmp目录 可以通过http://localhost:8082/uploadImg/aa.jpg访问。访问测试
在这里插入图片描述
ok,到了这一步还是有问题的,在前面我们将 addResourceLocations("file:D://tmp//")写死了,细想一下,它如果是linux系统呢?是不是还要继续改进。

02、改进

WebMvcConfiguration 配置类改进

package com.qd.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * 静态资源映射配置类
 *
 * @Author: qiandu
 * @Blog: https://www.cnblogs.com/qd666
 * @Date: 2021/11/22 5:50
 */
@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {

    @Value("${file.staticPatterPath}")
    private String staticPatterPath;
    @Value("${file.uploadFolder}")
    private String uploadFolder;
    
    // 配置文件上传的额外的静态资源配置
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        //  registry.addResourceHandler("/资源的访问路径").addResourceLocations("映射目录");
        registry.addResourceHandler(staticPatterPath + "**").addResourceLocations("file:" + uploadFolder);
    }
}

新建yml文件作环境隔离

# 本机配置
file:
  rootPath: http://localhost:8082
  staticPatterPath: /uploadImg/
  uploadFolder: D:/tmp/


# 服务器配置
file:
  rootPath: https://www.xxx.com
  staticPatterPath: /uploadImg/
  uploadFolder: /www/upload/

改进service

package com.qd.service;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;

/**
 * 文件上传
 *
 * @Author: qiandu
 * @Blog: https://www.cnblogs.com/qd666
 * @Date: 2021/11/21 21:20
 */
@Service
public class UploadService {

    // 根路径
    @Value("${file.rootPath}")
    private String rootPath;
    // 映射目录
    @Value("${file.uploadFolder}")
    private String uploadFolder;
    // 资源的访问路径
    @Value("${file.staticPatterPath}")
    private String staticPatterPath;

    /**
     * MultipartFile 对象是springMVC提供的文件上传接收的类
     * 文件上传底层原理 request.getInpuStream()
     *
     * @param multipartFile
     * @param dir
     * @return
     */
    public String uploadImg(MultipartFile multipartFile, String dir) {

        try {
            // 1:真实的文件名称
            String originalFilename = multipartFile.getOriginalFilename(); // 上传的文件aa.jpg
            // 2:截取后的文件名称 .jpg
            String imgSuffix = originalFilename.substring(originalFilename.lastIndexOf(".")); // 得到.jpg
            // 3:生成唯一的文件名称
            String newFileName = UUID.randomUUID().toString() + imgSuffix;  // 随机生成如:dfasf42432.jpg
            // 4:日期作为目录隔离文件
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd");
            String datePath = dateFormat.format(new Date()); // 日期目录:2021/11/21
            // 5:最终文件的上传目录
            File targetFile = new File(uploadFolder + dir, datePath); // 生成的最终目录; D://tmp/avatar/2021/11/21
            // 6:如果dirFile不存在,则创建
            if (!targetFile.exists()) targetFile.mkdirs();
            // 7: 指定文件上传后完整的文件名
            File dirFileName = new File(targetFile, newFileName); // 文件在服务器的最终路径是:D://tmp/avatar/2021/11/21/dfasf42432.jpg
            // 8:文件上传
            multipartFile.transferTo(dirFileName);

            // 9:可访问的路径 http://localhost:8082/avatar/2021/11/21/dfasf42432.jpg
            String fileName = dir + "/" + datePath + "/" + newFileName;
            return rootPath +staticPatterPath+ fileName;
        } catch (IOException e) {
            e.printStackTrace();
            return "fail";
        }
    }
}

03、获取文件多个信息

返回map即可

UploadService

package com.qd.service;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 文件上传
 *
 * @Author: qiandu
 * @Blog: https://www.cnblogs.com/qd666
 * @Date: 2021/11/21 21:20
 */
@Service
public class UploadService {

    // 根路径
    @Value("${file.rootPath}")
    private String rootPath;
    // 映射目录
    @Value("${file.uploadFolder}")
    private String uploadFolder;
    // 资源的访问路径
    @Value("${file.staticPatterPath}")
    private String staticPatterPath;

    /**
     * MultipartFile 对象是springMVC提供的文件上传接收的类
     * 文件上传底层原理 request.getInpuStream()
     *
     * @param multipartFile
     * @param dir
     * @return
     */
    public Map uploadImgMap(MultipartFile multipartFile, String dir) {

        try {
            // 1:真实的文件名称
            String originalFilename = multipartFile.getOriginalFilename(); // 上传的文件aa.jpg
            // 2:截取后的文件名称 .jpg
            String imgSuffix = originalFilename.substring(originalFilename.lastIndexOf(".")); // 得到.jpg
            // 3:生成唯一的文件名称
            String newFileName = UUID.randomUUID().toString() + imgSuffix;  // 随机生成如:dfasf42432.jpg
            // 4:日期作为目录隔离文件
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd");
            String datePath = dateFormat.format(new Date()); // 日期目录:2021/11/21
            // 5:最终文件的上传目录
            File targetFile = new File(uploadFolder + dir, datePath); // 生成的最终目录; D://tmp/avatar/2021/11/21
            // 6:如果dirFile不存在,则创建
            if (!targetFile.exists()) targetFile.mkdirs();
            // 7: 指定文件上传后完整的文件名
            File dirFileName = new File(targetFile, newFileName); // 文件在服务器的最终路径是:D://tmp/avatar/2021/11/21/dfasf42432.jpg
            // 8:文件上传
            multipartFile.transferTo(dirFileName);

            // 9:可访问的路径 http://localhost:8082/avatar/2021/11/21/dfasf42432.jpg
            String fileName = dir + "/" + datePath + "/" + newFileName;
            Map map = new HashMap<>();
            map.put("url", rootPath + staticPatterPath + fileName); // url
            map.put("size", multipartFile.getSize());// 大小
            map.put("fileName", originalFilename);// 真实文件名称
            map.put("ext", imgSuffix);// 后缀名
            return map;
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }
}

UploadController

package com.qd.controller;

import com.qd.service.UploadService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
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.util.Map;

/**
 * Create by IntelliJ IDEA
 *
 * @Author: qiandu
 * @Blog: https://www.cnblogs.com/qd666
 * @Date: 2021/11/21 21:01
 */
@Controller
public class UploadController {

    @Autowired
    private UploadService uploadService;

    @GetMapping("/upload")
    public String toUpload() {
        return "upload";
    }
    
    @PostMapping("/upload/file2")
    @ResponseBody
    public Map uploadMap(@RequestParam("file") MultipartFile multipartFile, HttpServletRequest request) {

        // 1:空判断
        if (multipartFile.isEmpty()) {
            return null;
        }

        // 2:判断文件是否符合
        //String contentType = multipartFile.getContentType();//得到文件类型
        //if (!"image/png".equals(contentType) || !"image/jpg".equals(contentType)) {
        //    return "文件格式不符合";
        //}

        // 3:获取用户指定的文件夹
        // 目的:作隔离,不同业务、不同文件放入到不同的目录中
        String dir = request.getParameter("dir");
        return uploadService.uploadImgMap(multipartFile, dir);
    }
}

前端

    

文件上传

结果
在这里插入图片描述
ok~至此,关于本机的文件上传就探讨到此,后续我们将使用阿里云oss存储继续学习文件上传。