• SpringBoot 集成MongoDB实现文件上传


    前言

    • 记录下SpringBoot集成MongoDB实现文件上传的步骤

    • MongoDB - 5.0.6安装包

    链接:https://pan.baidu.com/s/1lCcPvYYNWncb6lbvrZdeVg 
    提取码:0tf1
    

    环境

    SpringBoot - 2.5.12
    MongoDB - 5.0.6
    

    代码实现

    • pom.xml
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- mongodb -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-mongodb</artifactId>
    </dependency>
    <!-- lombok -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
    <!-- hutool -->
    <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-all</artifactId>
        <version>4.5.1</version>
    </dependency>
    
    • application.yml
    server:
      port: 31091
    
    spring:
      servlet:
        multipart:
          max-file-size: 100MB
      data:
        mongodb:
          host: 127.0.0.1
          port: 27017
          database: admin
          username: root
          password: sunday
    
    fileUploadService:
      impl: fileMongoServiceImpl
    
    • MongoConfig.java
    import com.mongodb.client.MongoClient;
    import com.mongodb.client.MongoDatabase;
    import com.mongodb.client.gridfs.GridFSBucket;
    import com.mongodb.client.gridfs.GridFSBuckets;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    /**
     * @Description MongoDB配置类
     */
    @Configuration
    public class MongoConfig {
        /**
         * 数据库配置信息
         */
        @Value("${spring.data.mongodb.database}")
        private String db;
    
        /**
         * GridFSBucket用于打开下载流
         * @param mongoClient
         * @return
         */
        @Bean
        public GridFSBucket getGridFSBucket(MongoClient mongoClient){
            MongoDatabase mongoDatabase = mongoClient.getDatabase(db);
            return GridFSBuckets.create(mongoDatabase);
        }
    }
    
    • FileUploadController.java
    import com.coisini.mongodb.model.ResponseMessage;
    import com.coisini.mongodb.service.FileUploadService;
    import com.coisini.mongodb.vo.FileExportVo;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.http.HttpHeaders;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.ResponseEntity;
    import org.springframework.web.bind.annotation.*;
    import org.springframework.web.multipart.MultipartFile;
    import javax.annotation.Resource;
    import java.util.List;
    import java.util.Objects;
    
    /**
     * @Description 文件上传接口
     */
    @Slf4j
    @RestController
    @RequestMapping("/file")
    public class FileUploadController {
    
        /**
         * 文件上传实现类
         */
        @Resource(name="${fileUploadService.impl}")
        private FileUploadService fileUploadService;
    
        /**
         * 文件上传
         * @param file
         * @return
         */
        @PostMapping("/upload")
        public ResponseMessage<?> uploadFile(@RequestParam(value = "file") MultipartFile file) {
            try {
                return ResponseMessage.ok("上传成功", fileUploadService.uploadFile(file));
            } catch (Exception e) {
                log.error("文件上传失败:", e);
                return ResponseMessage.error(e.getMessage());
            }
        }
    
        /**
         * 多文件上传
         * @param files
         * @return
         */
        @PostMapping("/uploadFiles")
        public ResponseMessage<?> uploadFile(@RequestParam(value = "files") List<MultipartFile> files) {
            try {
                return ResponseMessage.ok("上传成功", fileUploadService.uploadFiles(files));
            } catch (Exception e) {
                return ResponseMessage.error(e.getMessage());
            }
        }
    
        /**
         * 文件下载
         * @param fileId
         * @return
         */
        @GetMapping("/download/{fileId}")
        public ResponseEntity<Object> fileDownload(@PathVariable(name = "fileId") String fileId) {
            FileExportVo fileExportVo = fileUploadService.downloadFile(fileId);
    
            if (Objects.nonNull(fileExportVo)) {
                return ResponseEntity.ok()
                        .header(HttpHeaders.CONTENT_DISPOSITION, "fileName=\"" + fileExportVo.getFileName() + "\"")
                        .header(HttpHeaders.CONTENT_TYPE, fileExportVo.getContentType())
                        .header(HttpHeaders.CONTENT_LENGTH, fileExportVo.getFileSize() + "").header("Connection", "close")
                        .body(fileExportVo.getData());
            } else {
                return ResponseEntity.status(HttpStatus.NOT_FOUND).body("file does not exist");
            }
        }
    
        /**
         * 文件删除
         * @param fileId
         * @return
         */
        @DeleteMapping("/remove/{fileId}")
        public ResponseMessage<?> removeFile(@PathVariable(name = "fileId") String fileId) {
            fileUploadService.removeFile(fileId);
            return ResponseMessage.ok("删除成功");
        }
    
    }
    
    • FileUploadService.java
    import com.coisini.mongodb.vo.FileExportVo;
    import org.springframework.web.multipart.MultipartFile;
    import java.util.List;
    
    /**
     * @Description 文件上传接口
     */
    public interface FileUploadService {
    
        /**
         * 文件上传
         * @param file
         * @return
         */
        FileExportVo uploadFile(MultipartFile file) throws Exception;
    
        /**
         * 多文件上传
         * @param files
         * @return
         */
        List<FileExportVo> uploadFiles(List<MultipartFile> files);
    
        /**
         * 文件下载
         * @param fileId
         * @return
         */
        FileExportVo downloadFile(String fileId);
    
        /**
         * 文件删除
         * @param fileId
         */
        void removeFile(String fileId);
    
    }
    
    • FileMongoServiceImpl.java
    import cn.hutool.core.io.IoUtil;
    import cn.hutool.core.util.IdUtil;
    import com.coisini.mongodb.model.MongoFile;
    import com.coisini.mongodb.repository.MongoFileRepository;
    import com.coisini.mongodb.service.FileUploadService;
    import com.coisini.mongodb.util.MD5Util;
    import com.coisini.mongodb.vo.FileExportVo;
    import com.mongodb.client.gridfs.GridFSBucket;
    import com.mongodb.client.gridfs.GridFSDownloadStream;
    import com.mongodb.client.gridfs.model.GridFSFile;
    import lombok.RequiredArgsConstructor;
    import lombok.extern.slf4j.Slf4j;
    import org.bson.types.Binary;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.mongodb.core.MongoTemplate;
    import org.springframework.data.mongodb.core.query.Criteria;
    import org.springframework.data.mongodb.core.query.Query;
    import org.springframework.data.mongodb.gridfs.GridFsResource;
    import org.springframework.data.mongodb.gridfs.GridFsTemplate;
    import org.springframework.stereotype.Service;
    import org.springframework.web.multipart.MultipartFile;
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.Date;
    import java.util.List;
    import java.util.Objects;
    import java.util.Optional;
    import java.util.stream.Collectors;
    
    /**
     * @Description MongoDB文件上传实现类
     */
    @Slf4j
    @Service("fileMongoServiceImpl")
    @RequiredArgsConstructor(onConstructor = @__(@Autowired))
    public class FileMongoServiceImpl implements FileUploadService {
    
        private final MongoFileRepository mongoFileRepository;
        private final MongoTemplate mongoTemplate;
        private final GridFsTemplate gridFsTemplate;
        private final GridFSBucket gridFSBucket;
    
        /**
         * 多文件上传
         * @param files
         * @return
         */
        @Override
        public List<FileExportVo> uploadFiles(List<MultipartFile> files) {
    
            return files.stream().map(file -> {
                try {
                    return this.uploadFile(file);
                } catch (Exception e) {
                    log.error("文件上传失败", e);
                    return null;
                }
            }).filter(Objects::nonNull).collect(Collectors.toList());
        }
    
        /**
         * 文件上传
         * @param file
         * @return
         * @throws Exception
         */
        @Override
        public FileExportVo uploadFile(MultipartFile file) throws Exception {
            if (file.getSize() > 16777216) {
                return this.saveGridFsFile(file);
            } else {
                return this.saveBinaryFile(file);
            }
        }
    
        /**
         * 文件下载
         * @param fileId
         * @return
         */
        @Override
        public FileExportVo downloadFile(String fileId) {
            Optional<MongoFile> option = this.getBinaryFileById(fileId);
    
            if (option.isPresent()) {
                MongoFile mongoFile = option.get();
                if(Objects.isNull(mongoFile.getContent())){
                    option = this.getGridFsFileById(fileId);
                }
            }
    
            return option.map(FileExportVo::new).orElse(null);
        }
    
        /**
         * 文件删除
         * @param fileId
         */
        @Override
        public void removeFile(String fileId) {
            Optional<MongoFile> option = this.getBinaryFileById(fileId);
    
            if (option.isPresent()) {
                if (Objects.nonNull(option.get().getGridFsId())) {
                    this.removeGridFsFile(fileId);
                } else {
                    this.removeBinaryFile(fileId);
                }
            }
        }
    
        /**
         * 删除Binary文件
         * @param fileId
         */
        public void removeBinaryFile(String fileId) {
            mongoFileRepository.deleteById(fileId);
        }
    
        /**
         * 删除GridFs文件
         * @param fileId
         */
        public void removeGridFsFile(String fileId) {
            // TODO 根据id查询文件
            MongoFile mongoFile = mongoTemplate.findById(fileId, MongoFile.class );
            if(Objects.nonNull(mongoFile)){
                // TODO 根据文件ID删除fs.files和fs.chunks中的记录
                Query deleteFileQuery = new Query().addCriteria(Criteria.where("filename").is(mongoFile.getGridFsId()));
                gridFsTemplate.delete(deleteFileQuery);
                // TODO 删除集合mongoFile中的数据
                Query deleteQuery = new Query(Criteria.where("id").is(fileId));
                mongoTemplate.remove(deleteQuery, MongoFile.class);
            }
        }
    
        /**
         * 保存Binary文件(小文件)
         * @param file
         * @return
         * @throws Exception
         */
        public FileExportVo saveBinaryFile(MultipartFile file) throws Exception {
    
            String suffix = getFileSuffix(file);
    
            MongoFile mongoFile = mongoFileRepository.save(
                    MongoFile.builder()
                            .fileName(file.getOriginalFilename())
                            .fileSize(file.getSize())
                            .content(new Binary(file.getBytes()))
                            .contentType(file.getContentType())
                            .uploadDate(new Date())
                            .suffix(suffix)
                            .md5(MD5Util.getMD5(file.getInputStream()))
                            .build()
            );
    
            return new FileExportVo(mongoFile);
        }
    
        /**
         * 保存GridFs文件(大文件)
         * @param file
         * @return
         * @throws Exception
         */
        public FileExportVo saveGridFsFile(MultipartFile file) throws Exception {
            String suffix = getFileSuffix(file);
    
            String gridFsId = this.storeFileToGridFS(file.getInputStream(), file.getContentType());
    
            MongoFile mongoFile = mongoTemplate.save(
                    MongoFile.builder()
                            .fileName(file.getOriginalFilename())
                            .fileSize(file.getSize())
                            .contentType(file.getContentType())
                            .uploadDate(new Date())
                            .suffix(suffix)
                            .md5(MD5Util.getMD5(file.getInputStream()))
                            .gridFsId(gridFsId)
                            .build()
            );
    
            return new FileExportVo(mongoFile);
        }
    
        /**
         * 上传文件到Mongodb的GridFs中
         * @param in
         * @param contentType
         * @return
         */
        public String storeFileToGridFS(InputStream in, String contentType){
            String gridFsId = IdUtil.simpleUUID();
            // TODO 将文件存储进GridFS中
            gridFsTemplate.store(in, gridFsId , contentType);
            return gridFsId;
        }
    
        /**
         * 获取Binary文件
         * @param id
         * @return
         */
        public Optional<MongoFile> getBinaryFileById(String id) {
            return mongoFileRepository.findById(id);
        }
    
        /**
         * 获取Grid文件
         * @param id
         * @return
         */
        public Optional<MongoFile> getGridFsFileById(String id){
            MongoFile mongoFile = mongoTemplate.findById(id , MongoFile.class );
            if(Objects.nonNull(mongoFile)){
                Query gridQuery = new Query().addCriteria(Criteria.where("filename").is(mongoFile.getGridFsId()));
                try {
                    // TODO 根据id查询文件
                    GridFSFile fsFile = gridFsTemplate.findOne(gridQuery);
                    // TODO 打开流下载对象
                    GridFSDownloadStream in = gridFSBucket.openDownloadStream(fsFile.getObjectId());
                    if(in.getGridFSFile().getLength() > 0){
                        // TODO 获取流对象
                        GridFsResource resource = new GridFsResource(fsFile, in);
                        // TODO 获取数据
                        mongoFile.setContent(new Binary(IoUtil.readBytes(resource.getInputStream())));
                        return Optional.of(mongoFile);
                    }else {
                        return Optional.empty();
                    }
                }catch (IOException e){
                    log.error("获取MongoDB大文件失败", e);
                }
            }
    
            return Optional.empty();
        }
    
        /**
         * 获取文件后缀
         * @param file
         * @return
         */
        private String getFileSuffix(MultipartFile file) {
            String suffix = "";
            if (Objects.requireNonNull(file.getOriginalFilename()).contains(".")) {
                suffix = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));
            }
            return suffix;
        }
    
    }
    
    • MongoFileRepository.java
    import com.coisini.mongodb.model.MongoFile;
    import org.springframework.data.mongodb.repository.MongoRepository;
    
    /**
     * @Description MongoDB文件仓储
     */
    public interface MongoFileRepository extends MongoRepository<MongoFile, String> {
    
    }
    
    • MongoFile.java
    import lombok.Builder;
    import lombok.Data;
    import org.bson.types.Binary;
    import org.springframework.data.annotation.Id;
    import org.springframework.data.mongodb.core.mapping.Document;
    import java.util.Date;
    
    /**
     * @Description MongoDB文件实体
     */
    @Document
    @Builder
    @Data
    public class MongoFile {
    
        /**
         * 主键
         */
        @Id
        public String id;
    
        /**
         * 文件名称
         */
        public String fileName;
    
        /**
         * 文件大小
         */
        public long fileSize;
    
        /**
         * 上传时间
         */
        public Date uploadDate;
    
        /**
         * MD5值
         */
        public String md5;
    
        /**
         * 文件内容
         */
        private Binary content;
    
        /**
         * 文件类型
         */
        public String contentType;
    
        /**
         * 文件后缀名
         */
        public String suffix;
    
        /**
         * 文件描述
         */
        public String description;
    
        /**
         * 大文件管理GridFS的ID
         */
        private String gridFsId;
    
    }
    
    • ResponseMessage.java
    /**
     * @Description 统一消息
     */
    public class ResponseMessage<T> {
    
        private String status;
        private String message;
        private T data;
    
        // 省略
    
    }
    
    • FileExportVo.java
    import java.util.Objects;
    
    /**
     * @Description 统一文件下载vo
     */
    @Data
    public class FileExportVo {
    
        private String fileId;
    
        private String fileName;
    
        private String contentType;
    
        private String suffix;
    
        private long fileSize;
    
        @JsonIgnore
        private byte[] data;
    
        public FileExportVo(MongoFile mongoFile) {
            BeanUtil.copyProperties(mongoFile, this);
            if (Objects.nonNull(mongoFile.getContent())) {
                this.data = mongoFile.getContent().getData();
            }
            this.fileId = mongoFile.getId();
        }
    
    }
    
    • MD5Util.java
    import java.io.IOException;
    import java.io.InputStream;
    import java.security.MessageDigest;
    import java.security.NoSuchAlgorithmException;
    
    /**
     * @Description MD5工具类
     */
    public class MD5Util {
        /**
         * 获取该输入流的MD5值
         */
        public static String getMD5(InputStream is) throws NoSuchAlgorithmException, IOException {
            StringBuffer md5 = new StringBuffer();
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] dataBytes = new byte[1024];
    
            int nread = 0;
            while ((nread = is.read(dataBytes)) != -1) {
                md.update(dataBytes, 0, nread);
            };
            byte[] mdbytes = md.digest();
    
            // convert the byte to hex format
            for (int i = 0; i < mdbytes.length; i++) {
                md5.append(Integer.toString((mdbytes[i] & 0xff) + 0x100, 16).substring(1));
            }
            return md5.toString();
        }
    }
    

    测试

    • 文件上传

    在这里插入图片描述


    • 多文件上传

    在这里插入图片描述


    • 文件下载

    在这里插入图片描述


    • 文件删除

    在这里插入图片描述



    源码


    - End -
    - 个人笔记 -
    - 仅供参考 -

  • 相关阅读:
    一、【注解】Spring注解@ComponentScan
    一致性Hash算法
    垃圾回收器搭配和调优
    JVM的逃逸分析
    简单理解垃圾回收
    类加载机制和双亲委派模型
    VMWare15下安装CentOS7
    HBase协处理器(1)
    依赖注入的三种方式
    Javascript-设计模式_装饰者模式
  • 原文地址:https://www.cnblogs.com/maggieq8324/p/16156040.html
Copyright © 2020-2023  润新知