一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達(dá)式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務(wù)器之家 - 編程語言 - Java教程 - SpringBoot 使用Mongo的GridFs實(shí)現(xiàn)分布式文件存儲(chǔ)操作

SpringBoot 使用Mongo的GridFs實(shí)現(xiàn)分布式文件存儲(chǔ)操作

2022-02-26 00:32老鄭來了 Java教程

這篇文章主要介紹了Spring Boot 使用Mongo的GridFs實(shí)現(xiàn)分布式文件存儲(chǔ)操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

前言

這段時(shí)間在公司實(shí)習(xí),安排給我一個(gè)任務(wù),讓在系統(tǒng)里實(shí)現(xiàn)一個(gè)知識(shí)庫的模塊,產(chǎn)品說,就像百度網(wǎng)盤那樣。。。我tm…,這不就是應(yīng)了那句話,“這個(gè)需求很簡(jiǎn)單,怎么實(shí)現(xiàn)我不管”。

可是我google小能手怎么會(huì)認(rèn)輸呢,本來還說研究一下FastDFS啥的,但是因?yàn)槲覀冺?xiàng)目用的Mongo作為數(shù)據(jù)庫,了解到Mongo自帶分布式文件系統(tǒng)GridFs,這簡(jiǎn)直天助我也。

 

GridFs介紹

什么時(shí)候使用GridFs

我們平時(shí)使用Mongo也可以直接把文件的二進(jìn)制保存在Document中,就相當(dāng)于mysql的blob格式,但是mongo限制Document最大為16MB,那我們超過16MB的文件可咋辦吶,就可以利用GridFs來存儲(chǔ)。

  • 在某些情況下,在MongoDB數(shù)據(jù)庫中存儲(chǔ)大文件可能比在系統(tǒng)級(jí)文件系統(tǒng)上更高效。
  • 如果文件系統(tǒng)限制目錄中的文件數(shù),則可以使用GridFS根據(jù)需要存儲(chǔ)任意數(shù)量的文件。如果要從大型文件的各個(gè)部分訪問信息而無需將整個(gè)文件加載到內(nèi)存中,可以使用GridFS調(diào)用文件的各個(gè)部分,而無需將整個(gè)文件讀入內(nèi)存。
  • 如果要保持文件和元數(shù)據(jù)在多個(gè)系統(tǒng)和設(shè)施中自動(dòng)同步和部署,可以使用GridFS。使用地理位置分散的副本集時(shí),MongoDB可以自動(dòng)將文件及其元數(shù)據(jù)分發(fā)到多個(gè) mongod實(shí)例和工具中。

GridFs的原理

GridFS不是將文件存儲(chǔ)在單個(gè)文檔中,而是將文件分成多個(gè)部分或塊,并將每個(gè)塊存儲(chǔ)為單獨(dú)的文檔。

GridFS使用兩個(gè)集合來存儲(chǔ)文件。一個(gè)集合存儲(chǔ)文件塊,另一個(gè)存儲(chǔ)文件元數(shù)據(jù)。

SpringBoot 使用Mongo的GridFs實(shí)現(xiàn)分布式文件存儲(chǔ)操作 SpringBoot 使用Mongo的GridFs實(shí)現(xiàn)分布式文件存儲(chǔ)操作

默認(rèn)情況下,每一個(gè)塊的大小為255kB; 但最后一個(gè)塊除外。最后一個(gè)塊只有必要的大小。類似地,不大于塊大小的文件只有最終塊,只使用所需的空間和一些額外的元數(shù)據(jù)。

當(dāng)查詢GridFS文件時(shí),驅(qū)動(dòng)程序?qū)⒏鶕?jù)需要重新組裝塊??梢詫?duì)通過GridFS存儲(chǔ)的文件執(zhí)行范圍查詢。還可以從文件的任意部分訪問信息,例如“跳過”到視頻或音頻文件的中間。

 

環(huán)境

  • Spring Boot 2.0.4
  • Maven 3.5
  • Java 1.8
  • MongoDB 4.0
  • Robo 1.3.1

 

引入依賴和項(xiàng)目配置

首先添加Mongo客戶端的依賴

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

然后編寫配置文件

spring:
application:
  name: demo
data:
  mongodb:
    uri: mongodb://root:你的密碼@localhost:27019
    authentication-database: admin
    database: 你的數(shù)據(jù)庫

 

使用GridFsTemplate操作GridFs

GridFsTemplate是Spring提供的專門操作GridFs的客戶端,提供了一系列開箱即用的方法

SpringBoot 使用Mongo的GridFs實(shí)現(xiàn)分布式文件存儲(chǔ)操作

只要把它注入到我們的Conteoller中,就可以愉快的CRUD了,需要注意的是獲取文件時(shí)要注入MongoDbFactory ,我們使用默認(rèn)配置的話,直接注入就好。

 
import com.mongodb.Block;
import com.mongodb.MongoClient;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.gridfs.GridFSBucket;
import com.mongodb.client.gridfs.GridFSBuckets;
import com.mongodb.client.gridfs.model.GridFSFile;
import com.mongodb.client.gridfs.model.GridFSUploadOptions;
import com.mongodb.gridfs.GridFS;
import com.mongodb.gridfs.GridFSDBFile;
import org.bson.BsonValue;
import org.bson.Document;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile; 
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.*;
 
@RestController
@RequestMapping("/files")
public class FileController {
	private Logger logger = LoggerFactory.getLogger(GridFsServiceImpl.class);
  //匹配文件ID的正則
  private static Pattern NUMBER_PATTERN = Pattern.compile("(?<==).*(?=})"); 
  @Autowired
  GridFsTemplate gridFsTemplate;
  @Autowired
  MongoDbFactory mongoDbFactory;

  /**
   * 上傳文件
   *
   * @param file 文件
   * @return 文件名和文件存儲(chǔ)的fileId鍵值對(duì)的Map
   */
  @RequestMapping(value = "/upload", method = RequestMethod.POST)
  public Map<String, ObjectId> upload(@RequestParam(value = "file") MultipartFile file) {
if(pathId == null || fileType == null || pathId.equals("") || fileType.equals("")){
          logger.debug("上傳文件出錯(cuò)");
          throw new RuntimeException("上傳文件出錯(cuò):pathId and fileType can not be null");
      }
      Map<String, String> map = new HashMap<>(1);
      String fileName = file.getOriginalFilename();
      //設(shè)置元數(shù)據(jù)
      DBObject metaData = new BasicDBObject();
      metaData.put("userId","1");
      metaData.put("fileExtension",FilenameUtils.getExtension(file.getOriginalFilename()));
      //存儲(chǔ)文件的擴(kuò)展名
     
      try {
          InputStream inputStream = file.getInputStream();
          ObjectId fileId = gridFsTemplate.store(inputStream, fileName,metaData);
          //這個(gè)getFileId是我自己封裝的獲取文件ID的方法
          map.put(file.getOriginalFilename(),getFileId(fileId.toString()));
      } catch (IOException e) {
          logger.debug("上傳文件失敗: "+file);
          throw new RuntimeException("上傳文件失?。?quot;+e.getMessage());
      }
      return map;
  }

  /**
   * 通過文件fileId下載文件
   *
   * @param fileId 文件fileId
   * @param response 文件輸出流
   */
  @RequestMapping(value = "/downLoadByFileId")
  public void downLoadByFileId(@RequestParam(value = "fileId") ObjectId fileId, HttpServletResponse response) {
GridFSFile gridFsFile = gridFsTemplate.findOne(new Query().addCriteria(Criteria.where("_id").is(fileId)));
      if (gridFsFile != null) {
          // mongo-java-driver3.x以上的版本就變成了這種方式獲取
          GridFSBucket bucket = GridFSBuckets.create(mongoDbFactory.getDb());
          GridFSDownloadStream gridFsDownloadStream = bucket.openDownloadStream(gridFsFile.getObjectId());
          GridFsResource gridFsResource = new GridFsResource(gridFsFile,gridFsDownloadStream);
          String fileName = gridFsFile.getFilename().replace(",", "");
          //處理中文文件名亂碼
          if (request.getHeader("User-Agent").toUpperCase().contains("MSIE") ||
                  request.getHeader("User-Agent").toUpperCase().contains("TRIDENT")
                  || request.getHeader("User-Agent").toUpperCase().contains("EDGE")) {
              fileName = java.net.URLEncoder.encode(fileName, "UTF-8");
          } else {
              //非IE瀏覽器的處理:
              fileName = new String(fileName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1);
          }
          response.setHeader("Content-Disposition", "inline;filename=\"" + fileName + "\"");
          response.setContentType(application/octet-stream);
          IOUtils.copy(gridFsResource.getInputStream(), response.getOutputStream());
      }else {
          logger.info("文件不存在");
      } 
  }
  
	/**
   * 根據(jù)文件名查詢文件列表
   * @param fileName 文件名
   * @return
   */
  @RequestMapping("/search")
  public List<Map<String,Object>> search(String filePath, String fileName) {
		//這里返回的是一個(gè)List<Map<String,Object>>
		//因?yàn)镚ridFSFindIterable 迭代出來的GridFSFile不能直接返回
      GridFSFindIterable gridFSFiles = gridFsTemplate.find(new Query().addCriteria
              (Criteria.where("filename").regex("^.*"+fileName+".*$")));
      return getGridFSFiles(gridFSFiles);
  }

  /**
   * 通過fileId刪除文件
   * @param fileId 文件ID
   * @return 成功為true, 失敗為false
   */
  @RequestMapping("/deleteFilesByObjectId")
  public boolean deleteFilesByObjectId(@RequestParam(value = "fileId") String fileId) {
       try {
          gridFsTemplate.delete(new Query().addCriteria(Criteria.where("_id").is(fileId)));
      }catch (Exception e){
          logger.debug("刪除文件失敗: fileId= "+fileId);
          throw new RuntimeException("刪除文件失?。?quot;+e.getMessage());
      }
  }
	/**
   * 根據(jù)GridFSFiles迭代器返回文件列表,因?yàn)椴荒苤苯臃祷?,所以寫了這個(gè)工具方法
   * @param gridFSFiles 從數(shù)據(jù)庫取出的文件集合
   * @return List<Map<String,Object>>
   */
  private List<Map<String,Object>> getGridFSFiles(GridFSFindIterable gridFSFiles){
      List<Map<String,Object>> result = new ArrayList<>();
      for (GridFSFile file : gridFSFiles) {
          HashMap<String,Object> map = new HashMap<>(6);
          map.put("fileId",getFileId(file.getId().toString()));
          map.put("fileName",file.getFilename());
          map.put("fileExtension",file.getMetadata().get("fileExtension"));
          map.put("fileSize",file.getLength()/1024);
          map.put("uploadTime",file.getUploadDate());
          map.put("user",file.getMetadata().get("userId"));
          result.add(map);
      }
      return result;
  }
  /**
   * 因?yàn)閺膍ongo中獲取的文件Id是BsonObjectId {value=5d7068bbcfaf962be4c7273f}的樣子
   * 需要字符串截取
   * @param bsonObjectId 數(shù)據(jù)庫文件的BsonObjectId
   */
  private String getFileId(String bsonObjectId) {
      Matcher m = NUMBER_PATTERN.matcher(bsonObjectId);
      if(!m.find()){
          return bsonObjectId;
      }
      return m.group();
  }
}

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持服務(wù)器之家。

原文鏈接:https://blog.csdn.net/weixin_40693633/article/details/100562377

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 国产成人精品s8sp视频 | 精品一区视频 | haodiaose在线精品免费视频 | 天干夜天天夜天干天ww | 日韩一级片免费观看 | 国产亚洲福利一区二区免费看 | 日韩大片在线 | 非洲黑人gay巨大 | 国产我不卡 | 美女被网站 | 国产视频福利 | 蝴蝶传媒免费安装 | 精品综合一区二区三区 | 亚欧国产 | 久久国产综合精品欧美 | 亚洲高清中文字幕精品不卡 | 国内体内she精视频免费 | 亚洲欧美日韩成人一区在线 | 久久国产精品无码视欧美 | 久久这里只有精品视频e | 欧美日韩亚洲区久久综合 | 香蕉91| 免费抽搐一进一出印度 | 日本wwxx| 无码AV毛片色欲欧洲美洲 | 天天操天天干天天舔 | 亚洲成av人影院 | 欧美se图 | 欧美日韩一区二区三区韩大 | 波多野结衣女教师在线观看 | www亚洲色图 | 国产精品免费小视频 | 91精品国产99久久 | 国产-第1页-草草影院 | 日本高清视频在线观看 | 日韩欧美一区二区不卡 | 国产成人在线视频 | 国产精品乱码高清在线观看 | 扒开老师挠尿口到崩溃刑罚 | 免费观看日本 | 四虎精品在线视频 |