分佈式專題-分佈式緩存技術之MongoDB04-基於MongoDB實現網絡雲盤實戰

前言

前面的章節,關於分佈式緩存技術,我們分析了《分佈式緩存技術之Redis的使用以及原理》、這一節,繼續來說說MongoDB。

關於MongoDB,一共五小節內容,分別是:

基本實現思路介紹

常見網盤有:百度網盤、騰訊微盤、360網盤、阿里OSS、七牛雲 …

本節內容就是通過MongoDB實現某盤的文件上傳下載的基本功能~

拋磚引玉

  1. 爲何要使用MongoDB來實現雲盤?
    GridFS在Java中應用,作爲緩存中間件,是一種結構化的非關係型數據庫,適合雲盤場景

  2. 雲盤因何而存在?
    來自我本人的一個項目,經過了改造,完全去Servlet化的框架(SpringBoot)
    前端使用原生的Bootstrap+layui實現,後端以SpringBoot實現,數據庫使用MongoDB實現。

  3. 個人經驗分享
    分享自己的經驗,相當於全公司所有項目中用到文件存儲依賴

技術準備:SpringBoot 2.0 + MongoDB 4.0 + Bootstrap3.0

設計思路

文件分類
公共資源public:/ 任何人都共享
回收站recycle:/ 根據權限,每個人都有一個回收站
我的文件my:/ 每個用戶一個虛擬雲盤

最終實現效果圖:
在這裏插入圖片描述

數據隔離

文件夾說明

  • 我的文件: 每個人對應一個文件夾,僅此而已(它只不過一串有規律字符串)
  • 匿名用戶 anonymous:本節代碼通過匿名登錄
  • 註冊用戶:通常以單點登錄實現,暫時未開發

規則說明
規定:根目錄path /

子目錄 /XX/XXx/AA/BB/EE
子目錄 /XX/XXx/AA/DD/CC
子目錄 /XX/XXx/CC/DD
path 包含 /XX/XXx/AA/

在創建文件夾時,同級目錄下不允許重名

MongoDB存文件風險大?

用戶的註冊量達到了,因爲磁盤上目錄裏面的文件數量是有上限的!

高效存儲解決方案

  • 磁盤存儲,MongoDB存儲
  • 虛擬空間管理
  • 多數據源操作

主要功能

  • 登錄註銷
  • 上傳
  • 下載(FlashReader,swf,Word、PDF、Excel、MP4、MP3、PPT)
  • 預覽
  • 查詢
  • 無限級目錄
  • FFMPEG /多媒體的互轉 Word 轉成 Swf、 MP4/WVM… /MP4.H264

核心代碼演示

數據庫設計

在這裏插入圖片描述

手寫核心業務代碼

.json ajax交互的格式
.file 針對文件本身的操作,預覽,下載

如果做動靜分離之後,我會用Nginx來承擔一些功能

通常,文件的存儲有3種

  1. 直接寫磁盤,通過nginx來訪問。 顯然,性能下降
  2. 分佈式文件系統,比如FastDFS 推薦方案,性能是最高的,易擴容,擴容比較難定
  3. MongoDB,你把它當做緩存,方便 不推薦(只是因爲恰好我自己做過)

登錄/註銷

登錄:

 @RequestMapping("/login.json")
    public ResponseEntity login(@RequestParam("loginName") String loginName,
                                @RequestParam("loginPass") String loginPass,
                                @RequestParam("iframe") String iframe,
                                @RequestParam("callback") String callback,
                                @RequestParam("jumpto") String jumpto){
        ResultMsg<?> data = memberService.login(loginName,loginPass);

        HttpHeaders headers = new HttpHeaders();
        headers.add("Cache-Control", "no-cache, no-store, must-revalidate");
        headers.add("Pragma", "no-cache");
        headers.add("Expires", "0");

        String json = JSON.toJSONString(data);

        if(!(null == jumpto || "".equals(jumpto))) {
            JSONObject obj = JSON.parseObject(json);
            obj.getJSONObject("data").put("jumpto", jumpto);
            json = obj.toString();
        }

        if("1".equals(iframe)) {
            StringBuffer returnStr = new StringBuffer();
            returnStr.append("window.parent." + ((callback == null) ? "callback" : callback) + "(" + json + ");");
            returnStr.insert(0, "<script type=\"text/javascript\">").append("</script>");

            return ResponseEntity
                    .ok()
                    .headers(headers)
                    .contentType(MediaType.parseMediaType("text/html"))
                    .body(returnStr.toString());
        }else{
            return ResponseEntity
                    .ok()
                    .headers(headers)
                    .contentType(MediaType.parseMediaType("application/json"))
                    .body(((callback == null) ? json : (callback +"(" + json + ")")));
        }
    }

在這裏插入圖片描述

註銷:

    @GetMapping("/logout.json")
    public Mono<Object> logout(){
        ResultMsg<?> result = memberService.logout(null);
        return Mono.just(result);
    }

效果演示:
在這裏插入圖片描述

上傳文件

 @RequestMapping("/upload/progress.json")
    public ResponseEntity progress(@RequestParam("X-Progress-ID") String progressId,
                                 @RequestParam("callback") String callback){

        Progress progress = new Progress();
        progress.setFinish(1);

        HttpHeaders headers = new HttpHeaders();
        headers.add("Cache-Control", "no-cache, no-store, must-revalidate");
        headers.add("Pragma", "no-cache");
        headers.add("Expires", "0");

        String json = JSON.toJSONString(progress);

        return ResponseEntity
                .ok()
                .headers(headers)
                .contentType(MediaType.parseMediaType("application/json"))
                .body(((callback == null) ? json : (callback +"(" + json + ")")));

    }

在這裏插入圖片描述

上傳成功後:
在這裏插入圖片描述

此時我們創建多層級文件夾,查看效果:
在這裏插入圖片描述
這裏就說明了,創建的文件夾實際上就是一串以唯一Id爲前綴的字符串而已,前綴採用MD5加密

在這裏插入圖片描述
並且在上傳的過程中,我會持久化文件到本地磁盤:
在這裏插入圖片描述

然後打開本地磁盤目錄:
在這裏插入圖片描述
d41d8cd98f00b204e9800998ecf8427e它就是文件的ID
在本地磁盤也是找到了對應的以三個字符分隔而成的目錄,實際上傳的文件也已經持久化到本地目錄

磁盤的讀寫性能是沒有內存的讀寫性能高的

下載

@RequestMapping("/download/{id:\\w+}.file")
    public ResponseEntity download(@PathVariable(name="id") String id){

        ResultMsg<?> resultMsg = uFileService.download(ExplorerConstants.ANONYMOUS,id);
        File file = (File) resultMsg.getData();

        MimetypesFileTypeMap mimeTypeMap = new MimetypesFileTypeMap();
        HttpHeaders headers = new HttpHeaders();
        try {

            headers.add("Cache-Control", "no-cache, no-store, must-revalidate");
            headers.add("Content-Disposition", "attachment; filename=" + new String(file.getName().getBytes("UTF-8"), "iso8859-1"));
            headers.add("Pragma", "no-cache");
            headers.add("Expires", "0");
            headers.add("Last-Modified", new Date().toString());
            headers.add("ETag", String.valueOf(System.currentTimeMillis()));
            headers.add("Content-Type",mimeTypeMap.getContentType(file));

        }catch (Exception e){
            e.printStackTrace();
        }
        return ResponseEntity
                .ok()
                .headers(headers)
                .contentLength(file.length())
                .contentType(MediaType.parseMediaType("application/x-msdownload"))
                .body(new FileSystemResource(file));
    }

在這裏插入圖片描述

瀏覽

    @GetMapping(value="/preview/{id:\\w+}.file")
    public ResponseEntity preview(@PathVariable(name="id") String id){

        ResultMsg<?> resultMsg = uFileService.download(ExplorerConstants.ANONYMOUS,id);
        File file = (File) resultMsg.getData();

        MimetypesFileTypeMap mimeTypeMap = new MimetypesFileTypeMap();
        HttpHeaders headers = new HttpHeaders();
        try {
            //預覽一定要設置Content-Type,否則打不開
            headers.add("Content-Type",mimeTypeMap.getContentType(file));

        }catch (Exception e){
            e.printStackTrace();
        }
        return ResponseEntity
                .ok()
                .headers(headers)
                .body(new FileSystemResource(file));
    }

後記

本節網盤代碼完整版下載地址:
https://github.com/harrypottry/test-mongo-explorer

當然,此代碼只是一個簡單的DEMO,很多功能未完善,後續有機會再維護~

更多架構知識,歡迎關注本套Java系列文章,地址導航Java架構師成長之路

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章