基于Mongodb的分布式文件存储实现

分布式文件存储的方案有很多,今天分享一个基于mongodb数据库来实现文件的存储,mongodb支持分布式部署,以此来实现文件的分布式存储。

基于 MongoDB GridFS 的分布式文件存储实现:从原理到实战

一、引言

当系统存在大量的图片、视频、文档等文件需要存储和管理时,对于分布式系统而言,如何高效、可靠地存储这些文件是一个关键问题。MongoDB 的 GridFS 作为一种分布式文件存储机制,为我们提供了一个优秀的解决方案。它基于 MongoDB 的分布式架构,能够轻松应对海量文件存储的挑战,同时提供了便捷的文件操作接口。

二、GridFS 原理剖析

GridFS 是 MongoDB 中用于存储大文件的一种规范。它将文件分割成多个较小的 chunks(默认大小为 256KB),并将这些 chunks 存储在 fs.chunks 集合中,而文件的元数据(如文件名、大小、创建时间、MIME 类型等)则存储在 fs.files 集合中。这样的设计不仅能够突破 MongoDB 单个文档大小的限制(默认 16MB),还能利用 MongoDB 的分布式特性,实现文件的分布式存储和高效读取。

例如,当我们上传一个 1GB 的视频文件时,GridFS 会将其切分为约 4096 个 256KB 的 chunks,然后将这些 chunks 分散存储在不同的 MongoDB 节点上,同时在 fs.files 集合中记录文件的相关信息。

三、Spring Boot 集成 GridFS

在实际项目中,我们通常使用 Spring Boot 与 MongoDB 结合,下面是具体的集成步骤与代码示例。

3.1 添加依赖

在 pom.xml 文件中添加 Spring Boot 与 MongoDB 相关依赖:

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-mongodb</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>
</dependencies>

3.2 配置 MongoDB 连接

在 application.properties 中配置 MongoDB 的连接信息:

spring.data.mongodb.uri=mongodb://localhost:27017/fs
spring.data.mongodb.database=fs

3.3 编写服务类

使用 GridFsTemplate 和 GridFSBucket 来实现文件的上传、下载、删除等操作:

@Service
publicclass MongoFsStoreService implements FsStoreService {privatefinal GridFsTemplate gridFsTemplate;private GridFSBucket gridFSBucket;public MongoFsStoreService(GridFsTemplate gridFsTemplate) {this.gridFsTemplate = gridFsTemplate;}@Autowired(required = false)public void setGridFSBucket(GridFSBucket gridFSBucket) {this.gridFSBucket = gridFSBucket;}/*** 上传文件* @param in* @param fileInfo* @return*/@Overridepublic FileInfo uploadFile(InputStream in, FileInfo fileInfo){ObjectId objectId = gridFsTemplate.store(in, fileInfo.getFileId(), fileInfo.getContentType(), fileInfo);fileInfo.setDataId(objectId.toString());return fileInfo;}/**** @param in* @param fileName* @return*/@Overridepublic FileInfo uploadFile(InputStream in, String fileName) {FileInfo fileInfo = FileInfo.fromStream(in, fileName);return uploadFile(in, fileInfo);}/**** @param fileId* @return*/@Overridepublic File downloadFile(String fileId){GridFsResource gridFsResource = download(fileId);if( gridFsResource != null ){GridFSFile gridFSFile = gridFsResource.getGridFSFile();FileInfo fileInfo = JsonHelper.convert(gridFSFile.getMetadata(), FileInfo.class);try(InputStream in = gridFsResource.getInputStream()) {return FileHelper.newFile( in, fileInfo.getFileId() ); //} catch (IOException e) {thrownew RuntimeException(e);}}returnnull;}/*** 查找文件* @param fileId* @return*/public GridFsResource download(String fileId) {GridFSFile gridFSFile = gridFsTemplate.findOne(Query.query(GridFsCriteria.whereFilename().is(fileId)));if (gridFSFile == null) {returnnull;}if( gridFSBucket == null ){return gridFsTemplate.getResource(gridFSFile.getFilename());}GridFSDownloadStream downloadStream = gridFSBucket.openDownloadStream(gridFSFile.getObjectId());returnnew GridFsResource(gridFSFile, downloadStream);}/*** 删除文件* @param fileId*/@Overridepublic void deleteFile(String fileId) {gridFsTemplate.delete(Query.query(GridFsCriteria.whereFilename().is(fileId)));}}

3.4 创建控制器

提供 REST API 接口,方便外部调用:

@RestController
@RequestMapping("/mongo")
publicclass MongoFsStoreController {privatefinal MongoFsStoreService mongoFsStoreService;public MongoFsStoreController(MongoFsStoreService mongoFsStoreService) {this.mongoFsStoreService = mongoFsStoreService;}/**** @param file* @return*/@RequestMapping("/upload")public ResponseEntity<Result> uploadFile(@RequestParam("file") MultipartFile file){try(InputStream in = file.getInputStream()){FileInfo fileInfo = convertMultipartFile(file);return ResponseEntity.ok( Result.ok(mongoFsStoreService.uploadFile(in, fileInfo)) );}catch (Exception e){return ResponseEntity.ok( Result.fail(HttpStatus.INTERNAL_SERVER_ERROR.value(), e.getMessage()) );}}private FileInfo convertMultipartFile(MultipartFile file){FileInfo fileInfo = new FileInfo();fileInfo.setType(FilenameUtils.getExtension(file.getOriginalFilename()));fileInfo.setFileId(UUID.randomUUID().toString() + "." + fileInfo.getType()); //fileInfo.setFileName(file.getOriginalFilename());fileInfo.setSize(file.getSize());fileInfo.setContentType(file.getContentType());fileInfo.setCreateTime(new Date());return fileInfo;}/**** @param fileId* @param response*/@RequestMapping("/download")public void downloadFile(@RequestParam("fileId") String fileId, HttpServletResponse response){File file = mongoFsStoreService.downloadFile(fileId);if( file != null ){response.setContentType("application/octet-stream");response.setHeader("Content-Disposition", "attachment; filename=\"" + file.getName() + "\"");try {FileUtils.copyFile(file, response.getOutputStream());} catch (IOException e) {thrownew RuntimeException(e);}}}@RequestMapping("/download/{fileId}")public ResponseEntity<InputStreamResource> download(@PathVariable("fileId") String fileId) throws IOException {GridFsResource resource = mongoFsStoreService.download(fileId);if( resource != null ){GridFSFile gridFSFile = resource.getGridFSFile();FileInfo fileInfo = JsonHelper.convert(gridFSFile.getMetadata(), FileInfo.class);return ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + fileInfo.getFileName() + "\"").contentLength(fileInfo.getSize())
//                    .contentType(MediaType.parseMediaType(fileInfo.getContentType())).body(new InputStreamResource(resource.getInputStream()));}
//        return ResponseEntity.noContent().build();return ResponseEntity.internalServerError().build();}/**** @param fileId* @return*/@RequestMapping("/delete")public ResponseEntity<String> deleteFile(@RequestParam("fileId") String fileId){mongoFsStoreService.deleteFile(fileId);return ResponseEntity.ok("删除成功");}

四、实战中的常见问题与解决方案

4.1 文件下载时的内存管理

在下载文件时,GridFSDownloadStream 提供了流式处理的能力,避免一次性将整个文件加载到内存中。我们可以通过 GridFsResource 将流包装后直接返回给客户端,实现边读边传,从而节省内存。例如:

// 正确:直接返回 InputStreamResource,边读边传
return ResponseEntity.ok().body(new InputStreamResource(resource.getInputStream()));

而应避免将整个文件读取到字节数组中再返回,如以下错误示例:

// 错误:将整个文件加载到内存再返回
byte[] content = resource.getInputStream().readAllBytes(); 
return ResponseEntity.ok().body(content);

五、总结

基于 MongoDB GridFS 的分布式文件存储方案,凭借其独特的文件分块存储原理和与 MongoDB 分布式架构的紧密结合,为我们提供了一种高效、可靠的文件存储方式。通过 Spring Boot 的集成,我们能够快速在项目中实现文件的上传、下载、查询和删除等功能。在实际应用过程中,我们需要关注内存管理、数据类型转换、时间类型处理等常见问题,并采用合适的解决方案。随着技术的不断发展,GridFS 也在持续优化和完善,将为更多的分布式文件存储场景提供强大的支持。

对于中小文件存储,GridFS 是一个简单高效的选择;对于超大规模文件或需要极致性能的场景,可以考虑结合对象存储(如 MinIO、S3)使用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/web/80891.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Linux】Linux安装并配置Redis

目录 1.安装 2.启动服务 3.配置 3.1.绑定地址 3.2.保护模式 3.3.持久化选项 3.3.1.RDB 持久化 3.3.2.AOF 持久化 3.3.3.如何选择 1.安装 Redis 可以从默认的 CentOS 软件仓库中安装。运行以下命令来安装 Redis sudo dnf install redis -y 响应如下 2.启动服务 安装完成后&…

python-数据可视化(大数据、数据分析、可视化图像、HTML页面)

通过 Python 读取 XLS 、CSV文件中的数据&#xff0c;对数据进行处理&#xff0c;然后生成包含柱状图、扇形图和折线图的 HTML 报告。这个方案使用了 pandas 处理数据&#xff0c;matplotlib 生成图表&#xff0c;并将图表嵌入到 HTML 页面中。 1.XSL文件生成可视化图像、生成h…

黑马点评相关知识总结

黑马点评的项目总结 主要就黑马点评项目里面的一些比较重要部分的一次总结&#xff0c;方便以后做复习。 基于Session实现短信登录 短信验证码登录 这部分使用常规的session来存储用户的登录状态&#xff0c;其中短信发送采取逻辑形式&#xff0c;并不配置云服务验证码功能。…

手搓四人麻将程序

一、麻将牌的表示 在麻将游戏中&#xff0c;总共有一百四十四张牌&#xff0c;这些牌被分为多个类别&#xff0c;每个类别又包含了不同的牌型。具体来说&#xff0c;麻将牌主要包括序数牌、字牌和花牌三大类。序数牌中&#xff0c;包含有万子、条子和筒子&#xff0c;每种花色…

【Java高阶面经:数据库篇】17、分库分表分页查询优化:告别慢查询与内存爆炸

一、分库分表基础&#xff1a;策略与中间件形态 1.1 分库分表核心策略 分库分表是应对海量数据存储和高并发访问的关键架构设计&#xff0c;其核心在于将数据分散到不同的数据库或表中&#xff0c;以突破单库单表的性能限制。常见的分库分表策略包括&#xff1a; 1.1.1 哈希…

贪心算法之跳跃游戏问题

问题背景 本文背景是leetcode的一道经典题目&#xff1a;跳跃游戏&#xff0c;描述如下&#xff1a; 给定一个非负整数数组 nums&#xff0c;初始位于数组的第一个位置&#xff08;下标0&#xff09;。数组中的每个元素表示在该位置可以跳跃的最大长度。判断是否能够到达最后…

Label Studio:开源标注神器

目录 一、Label Studio 是什么&#xff1f; 二、核心功能大揭秘 2.1 多类型数据全兼容 2.2 个性化定制随心配 2.3 团队协作超给力 2.4 机器学习巧集成 三、上手实操超简单 3.1 安装部署不头疼 3.1.1 Docker安装 3.1.2 pip安装 3.1.3 Anaconda安装 3.2 快速开启标注…

创建信任所有证书的HttpClient:Java 实现 HTTPS 接口调用,等效于curl -k

在 Java 生态中&#xff0c;HttpClient 和 Feign 都是调用第三方接口的常用工具&#xff0c;但它们的定位、设计理念和使用场景有显著差异。以下是详细对比&#xff1a; DIFF1. 定位与抽象层级 特性HttpClientFeign层级底层 HTTP 客户端库&#xff08;处理原始请求/响应&#…

从零基础到最佳实践:Vue.js 系列(7/10):《常用内置 API 与插件》

引言 Vue.js 是一款轻量且强大的前端框架&#xff0c;因其易用性和灵活性受到广泛欢迎。无论是初学者还是资深开发者&#xff0c;都可以通过其内置 API 和插件生态快速构建高效、可维护的 Web 应用。本文将从基础用法讲起&#xff0c;逐步深入到进阶技巧&#xff0c;结合大量实…

线性代数:AI大模型的数学基石

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家、CSDN平台优质创作者&#xff0c;高级开发工程师&#xff0c;数学专业&#xff0c;10年以上C/C, C#, Java等多种编程语言开发经验&#xff0c;拥有高级工程师证书&#xff1b;擅长C/C、C#等开发语言&#xff0c;熟悉Java常用开…

Java-System工具类深度解析

Java-System工具类深度解析 前言一、System 类概述1.1 基本定义与特点1.2 重要成员变量 二、标准输入输出功能2.1 标准输入&#xff08;System.in&#xff09;2.2 标准输出&#xff08;System.out&#xff09;2.3 标准错误输出&#xff08;System.err&#xff09; 三、系统属性…

删除用户凭证

Git 部分仓库无法操作&#xff0c;部分仓库没问题 问题出现 我用个人电脑修改了项目&#xff0c;提交了git。然后第二天在公司电脑git pull的时候失败&#xff0c;只有部分仓库&#xff0c;git colne直接失败&#xff0c;部分仓库无问题。 解决方式 删除git相关凭证&#xff…

19. 结合Selenium和YAML对页面实例化PO对象改造

19. 结合Selenium和YAML对页面实例化PO对象改造 一、架构升级核心思路 1.1 改造核心目标 # 原始PO模式&#xff1a;显式定义元素定位 username (id, ctl00_MainContent_username)# 改造后PO模式&#xff1a;动态属性访问 self.username.send_keys(Tester) # 自动触发元素定…

鸿蒙App开发学习路径

以下是一份系统的鸿蒙&#xff08;HarmonyOS&#xff09;App开发学习路径&#xff0c;适合从零开始逐步掌握相关技能&#xff1a; 1. 基础知识储备 1.1 理解鸿蒙系统 鸿蒙核心特性&#xff1a;分布式能力、一次开发多端部署、原子化服务、ArkUI框架。与Android/iOS的区别&…

spring boot启动报错:2002 - Can‘t connect to server on ‘192.168.10.212‘ (10061)

错误代码 10061 通常表明无法建立到指定服务器的网络连接。这个错误属于 Windows Sockets 错误代码&#xff0c;具体指的是无法建立网络连接&#xff0c;通常是因为目标地址不可达。以下是一些解决此问题的步骤&#xff1a; 检查 IP 地址和端口&#xff1a; 确保你输入的 IP …

ARMv7的NVIC中断优先级

1. 优先级模型 数值规则:数值越小,优先级越高(例如优先级0的异常比优先级1的异常更高);若多个异常的优先级相同,则 异常号(Exception Number) 较小的异常优先执行。固定优先级异常(不可配置):异常类型 优先级值 说明 Reset -3 最高优先级(系统复位) NMI -2 不可屏…

gitee错误处理总结

背景 如上图&#xff0c;根据图片中的 Git 错误提示&#xff0c;我们遇到的问题是 ​本地分支落后于远程分支&#xff0c;导致 git push 被拒绝。 ​问题原因​ 远程仓库的 master 分支有其他人推送的新提交&#xff0c;而您的本地 master 分支未同步这些更新&#xff08;即本…

阿里云合集(不定期更新)

一、阿里云申请免费域名证书流程&#xff1a;https://blog.csdn.net/humors221/article/details/143266059 二、阿里云发送国内短信怎样编程&#xff1a;https://blog.csdn.net/humors221/article/details/139544193 三、阿里云ECS服务器磁盘空间不足的几个文件&#xff1a;h…

leetcode239 滑动窗口最大值deque方式

这段文字描述的是使用单调队列&#xff08;Monotonic Queue&#xff09; 解决滑动窗口最大值问题的优化算法。我来简单解释一下&#xff1a; 核心思路 问题分析&#xff1a;在滑动窗口中&#xff0c;若存在两个下标 i < j 且 nums[i] ≤ nums[j]&#xff0c;则 nums[i] 永远…

小白的进阶之路系列之三----人工智能从初步到精通pytorch计算机视觉详解下

我们将继续计算机视觉内容的讲解。 我们已经知道了计算机视觉,用在什么地方,如何用Pytorch来处理数据,设定一些基础的设置以及模型。下面,我们将要解释剩下的部分,包括以下内容: 主题内容Model 1 :加入非线性实验是机器学习的很大一部分,让我们尝试通过添加非线性层来…