基于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
繁體地址,請注明出處:http://hk.pswp.cn/web/80891.shtml
英文地址,請注明出處:http://en.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 :加入非線性實驗是機器學習的很大一部分,讓我們嘗試通過添加非線性層來…