SpringBoot之MongoDB附件操作
前言
近期自己针对附件上传进一步学习,为了弥足项目中文件上传的漏洞,保证文件上传功能的健壮性和可用性,现在我将自己在这一块的心得总结如下:
一、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: localhost
- port: 27017
- database: feng
- fileUploadService:
- impl: fileMongoServiceImpl
三、MongoDB配置类
- /**
- * @Description MongoDB配置类
- * @author songwp
- * @date Apr 17, 2022
- * @version 1.0
- */
- @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);
- }
- }
四、MongoDB文件实体
- /**
- * @Description MongoDB文件实体
- * @author songwp
- * @date Apr 17, 2022
- * @version 1.0
- */
- @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;
- }
五、返回统一消息处理类
- /**
- * @Description 统一消息
- * @author songwp
- * @date Apr 17, 2022
- * @version 1.0
- */
- public class ResponseMessage<T> {
- private String status;
- private String message;
- private T data;
- public static ResponseMessage<?> ok() {
- return create("0", (String)null, (Object)null);
- }
- public static ResponseMessage<?> ok(String message) {
- return create("0", message, (Object)null);
- }
- public static <T> ResponseMessage<T> ok(String message, T data) {
- return create("0", message, data);
- }
- public static <T> ResponseMessage<T> ok(T data) {
- return create("0", (String)null, data);
- }
- public static ResponseMessage<?> error() {
- return create("1", (String)null, (Object)null);
- }
- public static ResponseMessage<?> error(String message) {
- return create("1", message, (Object)null);
- }
- public static <T> ResponseMessage<T> error(String message, T data) {
- return create("1", message, data);
- }
- private static <T> ResponseMessage<T> create(String status, String message, T data) {
- ResponseMessage<T> t = new ResponseMessage();
- t.setStatus(status);
- t.setMessage(message);
- t.setData(data);
- return t;
- }
- public ResponseMessage() {
- }
- public String getStatus() {
- return this.status;
- }
- public String getMessage() {
- return this.message;
- }
- public T getData() {
- return this.data;
- }
- public void setStatus(final String status) {
- this.status = status;
- }
- public void setMessage(final String message) {
- this.message = message;
- }
- public void setData(final T data) {
- this.data = data;
- }
- }
六、统一文件下载vo
- /**
- * @Description 统一文件下载vo
- * @author songwp
- * @date Apr 8, 2022
- * @version 1.0
- */
- @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();
- }
- }
七、MD5工具类
- /**
- * @Description MD5工具类
- * @date Apr 8, 2022
- * @version 1.0
- */
- 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();
- }
- }
八、MongoDB文件仓储
- /**
- * @Description MongoDB文件仓储
- * @author songwp
- * @date Apr 17, 2022
- * @version 1.0
- */
- public interface MongoFileRepository extends MongoRepository<MongoFile, String> {
九、文件上传业务接口
- /**
- * @Description 文件上传接口
- * @author songwp
- * @date Apr 17, 2022
- * @version 1.0
- */
- 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);
- }
十、MongoDB文件上传实现类
- /**
- * @Description MongoDB文件上传实现类
- * @author songwp
- * @date Apr 17, 2022
- * @version 1.0
- */
- @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;
- }
十一、文件上传接口-控制器
- /**
- * @Description 文件上传接口
- * @author songwp
- * @date Apr 17, 2022
- * @version 1.0
- */
- @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("删除成功");
- }
- }
十二、接口测试
1.单个文件上传
2.多文件上传
3.文件下载
4.文件删除
SpringBoot之MongoDB附件操作的更多相关文章
- java操作mongodb & springboot整合mongodb
简单的研究原生API操作MongoDB以及封装的工具类操作,最后也会研究整合spring之后作为dao层的完整的操作. 1.原生的API操作 pom.xml <!-- https://mvnre ...
- 实例讲解Springboot整合MongoDB进行CRUD操作的两种方式
1 简介 Springboot是最简单的使用Spring的方式,而MongoDB是最流行的NoSQL数据库.两者在分布式.微服务架构中使用率极高,本文将用实例介绍如何在Springboot中整合Mon ...
- MongoDB系列:三、springboot整合mongoDB的简单demo
在上篇 MongoDB常用操作练习 中,我们在命令提示符窗口使用简单的mongdb的方法操作数据库,实现增删改查及其他的功能.在本篇中,我们将mongodb与spring boot进行整合,也就是在j ...
- SpringBoot与mongodb的结合
本文系列文章: 使用Shell 操作 MongoDB的技巧 MongoTemplate的使用技巧及其注意事项 敬请期待. 前言 最近公司想要做一个用户行为数据的收集,最开始想用mysql来存储 ...
- SpringBoot结合MongoDB入门
MongoDB 是一个基于分布式文件存储的数据库.由 C++ 语言编写.旨在为 WEB 应用提供可扩展的高性能数据存储解决方案. MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系 ...
- SpringBoot 整合mongoDB并自定义连接池
SpringBoot 整合mongoDB并自定义连接池 得力于SpringBoot的特性,整合mongoDB是很容易的,我们整合mongoDB的目的就是想用它给我们提供的mongoTemplate,它 ...
- springboot连接mongodb进行CRUD
springboot连接mongodb进行CRUD的过程: 在执行以下操作前已安装了mongodb并创建了用户和数据库,使用Robo 3T可成功连接. 1.创建springboot项目,加入以下mav ...
- SpringBoot使用MongoDB异常问题
一 环境介绍 SpringBoot1.5.13.RELEASE(本地) Spring Data MongoDB Java 8 MongoDB(青云) 二 问题描述 使用Studio3T或者Compas ...
- Springboot整合MongoDB的Docker开发,其它应用也类似
1 前言 Docker是容器开发的事实标准,而Springboot是Java微服务常用框架,二者必然是会走到一起的.本文将讲解如何开发Springboot项目,把它做成Docker镜像,并运行起来. ...
随机推荐
- XCTF练习题---WEB---get_post
XCTF练习题---WEB---get_post flag:cyberpeace{5526ac8044f1c5cfb5c421d34dff7822} 解题步骤: 1.观察题目,打开场景 2.观察页面内 ...
- socket编程实现tcp服务器_C/C++
1. 需求分析 实现一个回声服务器的C/S(客户端client/服务器server)程序,功能为客户端连接到服务器后,发送一串字符串,服务器接受信息后,返回对应字符串的大写形式给客户端显示. 例如: ...
- Unreal 输入系统 解析
前言 输入系统,输入某个键,响应到GamePlay层做对应的事.例如 点击鼠标,前进还是开枪之类,是如何响应的.这里只说应用层逻辑,硬件层逻辑不讲述. 详解 1.问题来源 先看下面一个例子:跳跃的事件 ...
- C#/VB.NET 实现Word和ODT文档相互转换
ODT文档格式一种开放文档格式(OpenDocument Text).通常,ODT格式的文件可以使用LibreOffice Writer.MS Word或其他一些文档编辑器来打开.我们在处理文档时,可 ...
- Redis GEO 地理位置
目录 GEO指令 GEOADD GEODIST GEOPOP GEOHASH GEORADIUS GEORADIUSBYMEMBER 指令补充 删除操作 避免单集合数量过多 存储原理 GEOADD存储 ...
- K8S面试应知必回
目录 面试不要不懂装懂,不会就是不会,不可能每个人都接触过所有的知识! 1. 基础问题 1.1 Service是怎么关联Pod的?(课程Service章节) 1.2 HPA V1 V2的区别 1.3 ...
- 【java并发编程】Lock & Condition 协调同步生产消费
一.协调生产/消费的需求 本文内容主要想向大家介绍一下Lock结合Condition的使用方法,为了更好的理解Lock锁与Condition锁信号,我们来手写一个ArrayBlockingQueue. ...
- 服务器/网络/虚拟化/云平台自动化运维-ansible
ansible与netconf的对比 首先明确一个概念,netconf是协议,ansible是python编写的工具 netconf 使用YANG建模,XML进行数据填充,使用netconf协议进行传 ...
- 忽略https域名校验不通过
curl curl 报错: curl: (51) Unable to communicate securely with peer: requested domain name does not ma ...
- supervisor安装以及监控管理rabbitmq消费者进程
简介:Supervisor是用Python开发的一套通用的进程管理程序,能将一个普通的命令行进程变为后台daemon,并监控进程状态,异常退出时能自动重启. 1.安装 apt-get install ...