分布式专题-分布式缓存技术之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架构师成长之路

猜你喜欢

转载自blog.csdn.net/qq_34361283/article/details/105906544