• 使用SpringBoot优雅的实现文件上传


    文件上传

    一、文件上传介绍

    文件上传,也称为upload,是指将本地图片、视频、音频等文件上传到服务器上,可以供其他用户浏览或下载的过程。文件上传在项目中应用非常广泛,我们经常发微博、发微信朋友圈都用到了文件上传功能。

    服务端要接收客户端页面上传的文件,通常都会使用Apache的两个组件:

    • commons-fileupload
    • commons-io

    Spring框架在spring-web包中对文件上传进行了封装,大大简化了服务端代码,我们只需要在Controller的方法中声明一个MultipartFile类型的参数即可接收上传的文件,例如:
    在这里插入图片描述

    二、 文件上传代码实现

    1. 编写数据表 file

    Sql:

    CREATE TABLE `sys_file` (
      `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
      `name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '文件名称',
      `type` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '文件类型',
      `size` bigint(20) DEFAULT NULL COMMENT '文件大小(kb)',
      `url` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '下载链接',
      `md5` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '文件md5',
      `is_delete` tinyint(1) DEFAULT '0' COMMENT '是否删除',
      `enable` tinyint(1) DEFAULT '1' COMMENT '是否禁用链接',
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=23 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    表结构:
    在这里插入图片描述

    其中is_delete和enable的默认值分别是0和1

    2. 后端代码编写

    文件上传的代码逻辑
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RHXSTNAi-1660664079789)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20220813220923285.png)]

    增加一个控制层类FileController.java:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D6nPKC8x-1660664079790)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20220812210259314.png)]

    在配置文件application.yml添加文件上传到的位置:

    files:
      upload:
        path: F:/后台管理系统/files/
    
    • 1
    • 2
    • 3

    文件磁盘路径:

    2.1 文件上传代码一

    /**
     * @author hj
     */
    @Slf4j
    @RestController
    @RequestMapping("/file")
    public class FileController {
    	//文件磁盘路径
        @Value("${files.upload.path}")
        private String fileUploadPath;
    
        @PostMapping("/upload")
        public Result upload(@RequestParam MultipartFile file) throws IOException {
            //获取文件原始名称
            String originalFilename = file.getOriginalFilename();
            //获取文件的类型
            String type = FileUtil.extName(originalFilename);
            log.info("文件类型是:" + type);
            //获取文件大小
            long size = file.getSize();
    
            //获取文件
            File uploadParentFile = new File(fileUploadPath);
            //判断文件目录是否存在
            if(!uploadParentFile.exists()) {
                //如果不存在就创建文件夹
                uploadParentFile.mkdirs();
            }
            //定义一个文件唯一标识码(UUID)
            String uuid = UUID.randomUUID().toString();
    
            File uploadFile = new File(fileUploadPath + uuid + StrUtil.DOT + type);
            //将临时文件转存到指定磁盘位置
            file.transferTo(uploadFile);
                
            return Result.success("");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    2.1.1 关键代码

    1. 将临时文件转存到指定位置

    //将临时文件转存到指定位置
    file.transferTo(new File(basePath + fileName));
    
    • 1
    • 2

    2. 注意:文件后缀(类型)要加上

    //获取文件的类型
    String type = FileUtil.extName(originalFilename);
    log.info("文件类型是:" + type);
    
    • 1
    • 2
    • 3

    StrUtil.DOT表示"."

    2.1.2 补充

    FileUtil类是一个工具类,需要配置hutool包的依赖

    <dependency>
        <groupId>cn.hutoolgroupId>
        <artifactId>hutool-allartifactId>
        <version>5.7.20version>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    2.1.3 测试

    我们现在apifox里面进行测试


    在这里插入图片描述

    文件已经创建:
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UaLvkBuh-1660664079794)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20220812213953789.png)]

    2.2 文件上传代码二(存储至数据库)

    2.2.1 编写实体类File.java
    @Getter
    @Setter
    @TableName("sys_file")
    @ApiModel(value = "File对象", description = "")
    public class File implements Serializable {
    
        private static final long serialVersionUID = 1L;
    
          @ApiModelProperty("id")
            @TableId(value = "id", type = IdType.AUTO)
          private Integer id;
    
          @ApiModelProperty("文件名称")
          private String name;
    
          @ApiModelProperty("文件类型")
          private String type;
    
          @ApiModelProperty("文件大小")
          private Long size;
    
          @ApiModelProperty("下载链接")
          private String url;
    
          @ApiModelProperty("是否删除")
          private Boolean isDelete;
    
          @ApiModelProperty("是否禁用链接")
          private Boolean enable;
          
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    2.2.2 FileController.java
    /**
     * 文件上传接口
     * @param file
     * @return
     * @throws IOException
     */
    @PostMapping("/upload")
    public String upload(@RequestParam MultipartFile file) throws IOException {
        //获取文件原始名称
        String originalFilename = file.getOriginalFilename();
        //获取文件的类型
        String type = FileUtil.extName(originalFilename);
        log.info("文件类型是:" + type);
        //获取文件大小
        long size = file.getSize();
    
        //文件存储的磁盘
        File uploadParentFile = new File(fileUploadPath);
        //判断文件目录是否存在
        if(!uploadParentFile.exists()) {
            //如果不存在就创建文件夹
            uploadParentFile.mkdirs();
        }
        //定义一个文件唯一标识码(UUID)
        String uuid = UUID.randomUUID().toString();
        String fileUUID = uuid + StrUtil.DOT + type;
        File uploadFile = new File(fileUploadPath + fileUUID);
    
        //将临时文件转存到指定磁盘位置
        file.transferTo(uploadFile);
    
        //设置下载的文件路径
        String url = "http://localhost:9090/file/" + fileUUID;
    
        //存储至数据库
        Files saveFile = new Files();
        saveFile.setName(originalFilename);
        saveFile.setType(type);
        saveFile.setSize(size/1024);//转成kb
        saveFile.setUrl(url);
        fileMapper.insert(saveFile);
    
        return url;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    2.3 优化代码

    在这里插入图片描述

    由于我们上传上去的图片有重复的,所以我们需要去重,只让他们共享一个图像

    去重的思路为:将文件的二进制流转换为MD5编码,每当我们上传一个文件就将其二进制流MD5与数据库当中已保存的文件的二进制流MD5进行比较,相同就舍弃,不相同就将文件的信息保存至数据库,文件内容上传至文件夹。

    /**
     * 文件上传接口
     * @param file
     * @return
     * @throws IOException
     */
    @PostMapping("/upload")
    public String upload(@RequestParam MultipartFile file) throws IOException {
        //获取文件原始名称
        String originalFilename = file.getOriginalFilename();
        //获取文件的类型
        String type = FileUtil.extName(originalFilename);
        log.info("文件类型是:" + type);
        //获取文件大小
        long size = file.getSize();
    
        //文件存储的磁盘
        File uploadParentFile = new File(fileUploadPath);
        //判断文件目录是否存在
        if(!uploadParentFile.exists()) {
            //如果不存在就创建文件夹
            uploadParentFile.mkdirs();
        }
    
        //定义一个文件唯一标识码(UUID)
        String uuid = UUID.randomUUID().toString();
        String fileUUID = uuid + StrUtil.DOT + type;
        File uploadFile = new File(fileUploadPath + fileUUID);
    
        String url;
        // 获取文件的md5
        String md5 = SecureUtil.md5(file.getInputStream());
        // 从数据库查询是否存在相同的记录
        Files dbFiles = fileService.getFileByMd5(md5);
        if (dbFiles != null) { // 文件已存在
            url = dbFiles.getUrl();
        } else {
            // 上传文件到磁盘
            file.transferTo(uploadFile);
            // 数据库若不存在重复文件,则不删除刚才上传的文件
            url = "http://localhost:9090/file/" + fileUUID;
        }
    
    
        //存储至数据库
        Files saveFile = new Files();
        saveFile.setName(originalFilename);
        saveFile.setType(type);
        saveFile.setSize(size/1024);//转成kb
        saveFile.setUrl(url);
        saveFile.setMd5(md5);
        fileService.save(saveFile);
    
        return url;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    FileServiceImpl.java

    @Service
    public class FileServiceImpl extends ServiceImpl<FileMapper, Files> implements IFileService {
    
        /**
         * 根据MD5查询文件
         * @param md5
         * @return
         */
        @Override
        public Files getFileByMd5(String md5) {
            LambdaQueryWrapper<Files> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(Files::getMd5, md5);
            List<Files> list = this.list(queryWrapper);
            return list.size() == 0 ? null : list.get(0);
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
  • 相关阅读:
    抖音矩阵系统。抖音矩阵系统。抖音矩阵系统。
    SpringCloud_OAuth 2.0 实现单点登录
    Centos7 安装gdal历程,使用node-gdal功能
    完美解决-RuntimeError: CUDA error: device-side assert triggered
    golang容易导致内存泄漏的几种情况
    Docker赋能物联网:探索软件供应链的优势、挑战和安全性
    『第十一章』数据持久化:CoreData 与 CloudKit
    DJ8-2 主存储器的组织
    如何让WPF中的ValidationRule实现参数绑定
    sqlalchemy expire_all 方法详解,强制刷新会话缓存
  • 原文地址:https://blog.csdn.net/Baridhu/article/details/126376517