`spring-boot-data-mongodb` 动态创建 `bucketName`
时间:2021-09-06
本文章向大家介绍`spring-boot-data-mongodb` 动态创建 `bucketName`,主要包括`spring-boot-data-mongodb` 动态创建 `bucketName`使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
当项目使用MongoDB
存储文件的时候,小于16M
是可以直接存在集合里面的,如果大于就需要用到GridFs
来存储了。
小于16M
存储的集合是可以运行时动态创建的,但是大于不可以。GridFs
默认的存储桶的名是fs
来定义的。具体可参考GridFSBucketImpl
实现类。这里对这个类不做过多介绍。这里主要介绍如何在程序运行时动态创建 bucketName
-
先看下
spring-boot-data-mongodb
提供的模板类GridFsTemplate
中具体方法。这里主要分析存储和查询的方法,该类的代码如下,该类有很多store
重载方法public class GridFsTemplate extends GridFsOperationsSupport implements GridFsOperations, ResourcePatternResolver { private final MongoDbFactory dbFactory; private final @Nullable String bucket; public GridFsTemplate(MongoDbFactory dbFactory, MongoConverter converter) { this(dbFactory, converter, null); } public GridFsTemplate(MongoDbFactory dbFactory, MongoConverter converter, @Nullable String bucket) { super(converter); Assert.notNull(dbFactory, "MongoDbFactory must not be null!"); this.dbFactory = dbFactory; this.bucket = bucket; } public ObjectId store(InputStream content, String filename) { return store(content, filename, (Object) null); } @Override public ObjectId store(InputStream content, @Nullable Object metadata) { return store(content, null, metadata); } @Override public ObjectId store(InputStream content, @Nullable Document metadata) { return store(content, null, metadata); } public ObjectId store(InputStream content, @Nullable String filename, @Nullable String contentType) { return store(content, filename, contentType, (Object) null); } public ObjectId store(InputStream content, @Nullable String filename, @Nullable Object metadata) { return store(content, filename, null, metadata); } public ObjectId store(InputStream content, @Nullable String filename, @Nullable String contentType, @Nullable Object metadata) { return store(content, filename, contentType, toDocument(metadata)); } public ObjectId store(InputStream content, @Nullable String filename, @Nullable Document metadata) { return this.store(content, filename, null, metadata); } /** * 主要储存实现 */ public ObjectId store(InputStream content, @Nullable String filename, @Nullable String contentType, @Nullable Document metadata) { Assert.notNull(content, "InputStream must not be null!"); // 主要getGridFs 返回一个 GridFSBucket,这个接口里面抽象大量的CURD的方法。具体你们可以看看 return getGridFs().uploadFromStream(filename, content, computeUploadOptionsFor(contentType, metadata)); } public GridFSFindIterable find(Query query) { Assert.notNull(query, "Query must not be null!"); Document queryObject = getMappedQuery(query.getQueryObject()); Document sortObject = getMappedQuery(query.getSortObject()); return getGridFs().find(queryObject).sort(sortObject); } public GridFSFile findOne(Query query) { return find(query).first(); } public void delete(Query query) { for (GridFSFile gridFSFile : find(query)) { getGridFs().delete(gridFSFile.getId()); } } public ClassLoader getClassLoader() { return dbFactory.getClass().getClassLoader(); } public GridFsResource getResource(String location) { return Optional.ofNullable(findOne(query(whereFilename().is(location)))) .map(this::getResource) .orElseGet(() -> GridFsResource.absent(location)); } public GridFsResource getResource(GridFSFile file) { Assert.notNull(file, "GridFSFile must not be null!"); return new GridFsResource(file, getGridFs().openDownloadStream(file.getId())); } public GridFsResource[] getResources(String locationPattern) { if (!StringUtils.hasText(locationPattern)) { return new GridFsResource[0]; } AntPath path = new AntPath(locationPattern); if (path.isPattern()) { GridFSFindIterable files = find(query(whereFilename().regex(path.toRegex()))); List<GridFsResource> resources = new ArrayList<>(); for (GridFSFile file : files) { resources.add(getResource(file)); } return resources.toArray(new GridFsResource[0]); } return new GridFsResource[]{getResource(locationPattern)}; } /** * 这个方法是最重要的 */ private GridFSBucket getGridFs() { MongoDatabase db = dbFactory.getDb(); /* * 判断 bucket 是否为null,是创建默认,否则根据传入bucket的创建GridFSBucket对象,默认创建的GridFsTemplate是不传入bucket * 具体可以参考MongoDbFactoryDependentConfiguration该类的bean创建实现 * * */ return bucket == null ? GridFSBuckets.create(db) : GridFSBuckets.create(db, bucket); } }
可以看到
GridFsTemplate
核心是getGridFs()
方法。该类里面的所有操作都是依赖该方法的。我们主要针对这个就可以实现运行时动态创建bucketName
了 -
我们自定义一个类继承
GridFsTemplate
即可,代码如下/** * 自定义 {@link GridFsTemplate} 实现,用于实现动态创建 bucket * * @author: pinlin * @date: 2021/9/1 17:03 */ public class CustomGridFsTemplate extends GridFsTemplate { private final MongoDbFactory dbFactory; public CustomGridFsTemplate(MongoDbFactory dbFactory, MongoConverter converter) { super(dbFactory, converter); this.dbFactory = dbFactory; } public CustomGridFsTemplate(MongoDbFactory dbFactory, MongoConverter converter, String bucket) { super(dbFactory, converter, bucket); this.dbFactory = dbFactory; } /** * 校验符合平台预设 * * @param bucketName MongoDB 桶的名字 具体参考 * @throws CustomSystemException 抛出自定义异常 * @author zpl * @date 2021/9/2 14:21 */ public static void isExist(String bucketName) throws CustomSystemException { if (StrUtil.isBlank(bucketName)) { throw new CustomSystemException("bucketName 名字不能为空"); } // GridFsBucketEnum该枚举是我定义MongoDB的枚举类,主要用于校验传入bucketName是否是平台预设的。安全着想 if (Boolean.FALSE.equals(GridFsBucketEnum.isExclude(bucketName))) { throw new CustomSystemException("bucketName 名字不符合平台预设"); } } /** * 存储文件 * * @param content 输入文件流 * @param filename 文件名 * @param contentType 文件类型 * @param bucketName 桶的名字 * @return {@link ObjectId} * @throws CustomSystemException 抛出非受检异常,外部注意捕获 * @author zpl * @date 2021/9/2 14:27 */ public ObjectId store(InputStream content, String filename, String contentType, String bucketName) throws CustomSystemException { return getGridFs(bucketName).uploadFromStream(filename, content, computeUploadOptionsFor(contentType, toDocument(null))); } /** * 创建 {@link GridFSBucket} 对象 * * @param bucketName MongoDB的桶的名字 * @return {@link GridFSBucket} * @author zpl * @date 2021/9/2 14:05 */ private GridFSBucket getGridFs(String bucketName) { isExist(bucketName); MongoDatabase db = dbFactory.getDb(); if (StrUtil.isNotBlank(bucketName)) { return GridFSBuckets.create(db, bucketName); } return GridFSBuckets.create(db); } /** * 查询多个 返回 {@link GridFSFindIterable} 对象 * * @param query 查询对象 {@link Query} * @param bucketName MongoDB桶的名字 * @return {@link GridFSFindIterable} * @author zpl * @date 2021/9/2 14:17 */ public GridFSFindIterable find(Query query, String bucketName) { Assert.notNull(query, "Query 不能为空!"); Assert.notNull(bucketName, "bucketName 不能为空!"); Document queryObject = getMappedQuery(query.getQueryObject()); Document sortObject = getMappedQuery(query.getSortObject()); return getGridFs(bucketName).find(queryObject).sort(sortObject); } /** * 查询单个 * * @param query 查询对象 {@link Query} * @param bucketName MongoDB桶的名字 * @return {@link GridFSFile} * @throws CustomSystemException 抛出非受检异常,外部注意捕获 * @author zpl * @date 2021/9/2 14:18 */ public GridFSFile findOne(Query query, String bucketName) { return find(query, bucketName).first(); } /** * 删除 * * @param query 查询对象 {@link Query} * @param bucketName MongoDB桶的名字 * @throws CustomSystemException 抛出非受检异常,外部注意捕获 * @author zpl * @date 2021/9/2 14:18 */ public void delete(Query query, String bucketName) { for (GridFSFile fsFile : find(query, bucketName)) { getGridFs(bucketName).delete(fsFile.getId()); } } }
-
把自定义的类装配到
spring
容器,方便管理以及使用,代码如下/** * {@link org.springframework.data.mongodb.gridfs.GridFsTemplate} 创建 * * @author: pinlin * @date: 2021/8/26 16:41 */ @Configuration public class GridFsTemplateConfig { @Bean(name = "gridFsTemplate") public CustomGridFsTemplate gridFsTemplate(MongoDbFactory mongoDbFactory, MongoConverter converter) { return new CustomGridFsTemplate(mongoDbFactory, converter); } }
说明
这个装配,如果是微服务工程,可以创建一个
mongodb
工程,把所有的封装都抽象在这个工程里面。然后写一个注解,使用spring-boot
的@Import
的注解导入GridFsTemplateConfig
就可以实现插拔装配自定义类 -
使用自定义配置类下载,代码如下
public class Test { @Autowired private CustomGridFsTemplate customGridFsTemplate; @Autowired private MongoDbFactory mongoDbFactory; public void download(@PathVariable("fileId") String fileId, @PathVariable("bucketName") String bucketName, HttpServletResponse response, HttpServletRequest request) throws IOException { Query query = Query.query(Criteria.where("_id").is(fileId)); // 查询根据传入的bucketName ,这个bucketName最后和后台预设的对比以及校验写,不然别人乱传,就会导致创建很多个bucket储存桶,不安全 GridFSFile gridFSFile = customGridFsTemplate.findOne(query, bucketName); if (gridFSFile != null) { GridFSBucket bucket = GridFSBuckets.create(mongoDbFactory.getDb(), bucketName); GridFSDownloadStream gridFSDownloadStream = bucket.openDownloadStream(gridFSFile.getObjectId()); GridFsResource gridFsResource = new GridFsResource(gridFSFile, gridFSDownloadStream); // 获取文件名 String fileName = FileUtils.getFileName(request, gridFSFile); response.setHeader(HttpHeaders.CONTENT_DISPOSITION, String.format("attachment;filename=\"%s\"", fileName)); response.setHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(gridFSFile.getLength())); response.setHeader(HttpHeaders.CONTENT_TYPE, "application/octet-stream;charset=ISO8859-1"); response.setHeader(HttpHeaders.CONTENT_TYPE, gridFsResource.getContentType()); IOUtils.copy(gridFsResource.getInputStream(), response.getOutputStream()); } } }
最后
此写法是我在项目中分析得到的。目前已经应用到我的项目中,如果你们有更好的方案,麻烦留言互相学习。如果指导有误的地方,还请指出。谢谢。
原文地址:https://www.cnblogs.com/zengpinlin/p/15235704.html
- 基于Cef内核的多店铺登录器(含源码)
- Android解析ClassLoader(二)Android中的ClassLoader
- 近期开发中遇到的问题总结
- Apache Spark中的决策树
- 数据结构是哈希表(hashTable)
- 【翻译】在Visual Studio中使用Asp.Net Core MVC创建你的第一个Web API应用(一)
- 基于JQuery EasyUI的WebMVC控件封装(含源码)
- Android系统源码分析-JNI
- EntityFrameWork实现部分字段获取和修改(含源码)
- 基于Ado.Net的日志组件
- Do you kown Asp.Net Core -- 配置Kestrel端口
- 【翻译】在Visual Studio中使用Asp.Net Core MVC创建第一个Web Api应用(二)
- 微信快速开发框架(一)-- 对微信公众平台开发的消息处理
- 微信快速开发框架(二) -- 快速开发微信公众平台框架---简介
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 解决SSH登录缓慢
- Html ul、li Css标签详解 使用图片自定义样式 隐藏小点样式齐全
- TRTC Android端开发接入学习之实现视频通话(五)
- 让WordPress默认用户无法进入后台
- 图像处理笔记(2)----OpenCV imread函数详解
- 面试官:来写个代码求一下两个数的最大公约数吧
- 聊聊BitCaskLock
- 自动化构建工具~Maven
- 聊聊BitCaskKeyDir
- 使用 Node.js 定制你的技术雷达:上篇
- css画div对角线
- redis学习笔记--redis过期机制学习
- 猿实战08——属性库实现之属性关系绑定
- 猿实战07——属性库之实现电商系统基石模块
- 栈与队列简介