溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

SpringBoot怎么使用Mongo的GridFs實現(xiàn)分布式文件存儲操作

發(fā)布時間:2021-10-23 13:38:59 來源:億速云 閱讀:175 作者:iii 欄目:開發(fā)技術(shù)

本篇內(nèi)容介紹了“SpringBoot怎么使用Mongo的GridFs實現(xiàn)分布式文件存儲操作”的有關(guān)知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細閱讀,能夠?qū)W有所成!

目錄
  • 前言

  • GridFs介紹

    • 什么時候使用GridFs

    • GridFs的原理

  • 環(huán)境

    • 引入依賴和項目配置

      • 使用GridFsTemplate操作GridFs

        前言

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

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

        GridFs介紹

        什么時候使用GridFs

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

        • 在某些情況下,在MongoDB數(shù)據(jù)庫中存儲大文件可能比在系統(tǒng)級文件系統(tǒng)上更高效。

        • 如果文件系統(tǒng)限制目錄中的文件數(shù),則可以使用GridFS根據(jù)需要存儲任意數(shù)量的文件。如果要從大型文件的各個部分訪問信息而無需將整個文件加載到內(nèi)存中,可以使用GridFS調(diào)用文件的各個部分,而無需將整個文件讀入內(nèi)存。

        • 如果要保持文件和元數(shù)據(jù)在多個系統(tǒng)和設(shè)施中自動同步和部署,可以使用GridFS。使用地理位置分散的副本集時,MongoDB可以自動將文件及其元數(shù)據(jù)分發(fā)到多個 mongod實例和工具中。

        GridFs的原理

        GridFS不是將文件存儲在單個文檔中,而是將文件分成多個部分或塊,并將每個塊存儲為單獨的文檔。

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

        SpringBoot怎么使用Mongo的GridFs實現(xiàn)分布式文件存儲操作 SpringBoot怎么使用Mongo的GridFs實現(xiàn)分布式文件存儲操作

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

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

        環(huán)境

        • Spring Boot 2.0.4

        • Maven 3.5

        • Java 1.8

        • MongoDB 4.0

        • Robo 1.3.1

        引入依賴和項目配置

        首先添加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實現(xiàn)分布式文件存儲操作

        只要把它注入到我們的Conteoller中,就可以愉快的CRUD了,需要注意的是獲取文件時要注入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 文件名和文件存儲的fileId鍵值對的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("上傳文件出錯");
                    throw new RuntimeException("上傳文件出錯: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()));
                //存儲文件的擴展名
               
                try {
                    InputStream inputStream = file.getInputStream();
                    ObjectId fileId = gridFsTemplate.store(inputStream, fileName,metaData);
                    //這個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) {
        		//這里返回的是一個List<Map<String,Object>>
        		//因為GridFSFindIterable 迭代出來的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迭代器返回文件列表,因為不能直接返回,所以寫了這個工具方法
             * @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;
            }
            /**
             * 因為從mongo中獲取的文件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();
            }
        }

        “SpringBoot怎么使用Mongo的GridFs實現(xiàn)分布式文件存儲操作”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實用文章!

        向AI問一下細節(jié)

        免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

        AI