当前位置:首页 > 生活百科

mongodb分布式部署(mongodb三种部署方式)

栏目:生活百科日期:2025-03-18浏览:0

实战:基于MongoDB文件服务器

本节,我们将介绍如何基于MongoDB技术来存储二进制文件,从而实现一个文件服务器MongoDB File Server。

文件服务器的需求

本文件服务器致力于小型文件的存储,比如博客中的图片、普通文档等。由于MongoDB支持多种数据格式的存储,对于二进制的存储自然也是不在话下,所以可以很方便地用于存储文件。由于MongoDB的BSON文档对于数据量大小的限制(每个文档不超过16MB),所以本文件服务器主要针对的是小型文件的存储。对于大型文件的存储(比如超过16MB),MongoDB官方已经提供了成熟的产品GridFS,读者朋友可以自行了解。

文件服务器应能够提供与平台无关的REST API供外部系统调用。

文件服务器整体的API设计如下。

·GET/files/{pageIndex}/{pageSize}:分页查询已经上传了的文件。

·GET/files/{id}:下载某个文件。

·GET/view/{id}:在线预览某个文件。比如,显示图片。

·POST/upload:上传文件。

·DELETE/{id}:删除文件。

我们创建一个新项目,称之为mongodb-file-server。

所需技术

本例子采用的开发技术如下。

·MongoDB 3.4.6。·Spring Boot 2.0.0.M2。

·Spring Data Mongodb 2.0.0.M4。

·Thymeleaf 3.0.6.RELEASE。

·Thymeleaf Layout Dialect 2.2.2。

·Embedded MongoDB 2.0.0。

其中,Spring Boot用于快速构建一个可独立运行的Java项目;

Thymeleaf作为前端页面模板,方便展示数据;Embedded MongoDB则是一款由Organization Flapdoodle OSS出品的内嵌MongoDB,可以在不启动MongoDB服务器的前提下,方便进行相关的MongoDB接口测试。

本文所演示的项目,是采用Gradle进行组织以及构建的,如果对Gradle不熟悉,也可以自行将项目转为Maven项目。

build.gradle文件完整配置内容如下。

buildscript { // buildscript 代码块中脚本优先执行// ext 用于定义动态属性ext {springBootVersion = '2.0.0.M2'}// 使用了 Maven 的中央仓库(也可以指定其他仓库)repositories {//mavenCentral()maven { url "https://repo.spring.io/snapshot" }maven { url "https://repo.spring.io/milestone" }maven { url "http://maven.aliyun.com/nexus/content/groups/public/" }}// 依赖关系dependencies {// classpath 声明说明了在执行其余的脚本时,ClassLoader 可以使用这些依赖项classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")}}// 使用插件apply plugin: 'java'apply plugin: 'eclipse'apply plugin: 'org.springframework.boot'apply plugin: 'io.spring.dependency-management'// 指定了生成的编译文件的版本,默认是打成了 jar 包version = '1.0.0'// 指定编译 .java 文件的 JDK 版本sourceCompatibility = 1.8// 使用了 Maven 的中央仓库(也可以指定其他仓库)repositories {//mavenCentral()maven { url "https://repo.spring.io/snapshot" }maven { url "https://repo.spring.io/milestone" }maven { url "http://maven.aliyun.com/nexus/content/groups/public/" }}// 依赖关系dependencies {// 该依赖用于编译阶段compile('org.springframework.boot:spring-boot-starter-web')// 添加 Thymeleaf 的依赖compile('org.springframework.boot:spring-boot-starter-thymeleaf')// 添加 Spring Data Mongodb 的依赖compile('org.springframework.boot:spring-boot-starter-data-mongodb')// 添加 Embedded MongoDB 的依赖用于测试compile('de.flapdoodle.embed:de.flapdoodle.embed.mongo')// 该依赖用于测试阶段testCompile('org.springframework.boot:spring-boot-starter-test')}

该build.gradle文件中的各配置项的注释已经非常详尽了,这里就不再赘述其配置项的含义了。

文件服务器的实现

在mongodb-file-server项目基础上,我们将实现文件服务器的功能。

1.领域对象

首先,我们要对文件服务器进行建模。相关的领域模型如下。

文档类是类似与JPA中的实体的概念。不同的是JPA是采用@Entity注解,而文档类是采用@Document注解。


com.waylau.spring.boot.fileserver.domain包下,我们创建了一个File类。

import org.bson.types.Binary;import org.springframework.data.annotation.Id;import org.springframework.data.mongodb.core.mapping.Document;...@Documentpublic class File {@Id // 主键private String id;private String name; // 文件名称private String contentType; // 文件类型private long size;private Date uploadDate;private String md5;private Binary content; // 文件内容private String path; // 文件路径// 省略 getter/setter 方法protected File() {}public File(String name, String contentType, long size,Binary content) {this.name = name;this.contentType = contentType;this.size = size;this.uploadDate = new Date();this.content = content;}@Overridepublic boolean equals(Object object) {if (this == object) {return true;}if (object == null || getClass() != object.getClass()) {return false;}File fileInfo = (File) object;return java.util.Objects.equals(size, fileInfo.size)&&&& java.util.Objects.equals(name, fileInfo.name)&&&& java.util.Objects.equals(contentType, fileInfo.contentType)&&&& java.util.Objects.equals(uploadDate, fileInfo.uploadDate)&&&& java.util.Objects.equals(md5, fileInfo.md5)&&&& java.util.Objects.equals(id, fileInfo.id);}@Overridepublic int hashCode() {return java.util.Objects.hash(name, contentType, size, uploadDate, md5, id);}@Overridepublic String toString() {return "File{"+ "name='" + name + '''+ ", contentType='" + contentType + '''+ ", size=" + size+ ", uploadDate=" + uploadDate+ ", md5='" + md5 + '''+ ", id='" + id + '''+ '}';}}

需要注意以下两点。

·文档类,主要采用的是Spring Data MongoDB中的注解,用于标识这是NoSQL中的文档概念。

·文件的内容,我们是用org.bson.types.Binary类型来进行存储。

2.存储库FileRepository

存储库用于提供与数据库“打交道”的常用的数据访问接口。其中FileRepository接口继承自
org.springframework.data.mongodb.repository.MongoRepository即可,无须自行实现该接口的功能,Spring Data MongoDB会自动实现接口中的方法。

import org.springframework.data.mongodb.repository.MongoRepository;import com.waylau.spring.boot.fileserver.domain.File;public interface FileRepository extends MongoRepository&<File, String&> {}

3.服务接口及实现类

FileService接口定义了对于文件的CURD操作,其中查询文件接口是采用的分页处理,以有效提升查询性能。

public interface FileService {/*** 保存文件* @param File* @return*/File saveFile(File file);/*** 删除文件* @param File* @return*/void removeFile(String id);/*** 根据id获取文件* @param File* @return*/File getFileById(String id);/*** 分页查询,按上传时间降序* @param pageIndex* @param pageSize* @return*/List&<File&> listFilesByPage(int pageIndex, int pageSize);}FileServiceImpl实现了FileService中所有的接口。@Servicepublic class FileServiceImpl implements FileService {@Autowiredpublic FileRepository fileRepository;@Overridepublic File saveFile(File file) {return fileRepository.save(file);}@Overridepublic void removeFile(String id) {fileRepository.deleteById(id);}@Overridepublic Optional&<File&> getFileById(String id) {return fileRepository.findById(id);}@Overridepublic List&<File&> listFilesByPage(int pageIndex, int pageSize) {Page&<File&> page = null;List&<File&> list = null;Sort sort = new Sort(Direction.DESC,"uploadDate");Pageable pageable = PageRequest.of(pageIndex, pageSize, sort);page = fileRepository.findAll(pageable);list = page.getContent();return list;}}

4.控制层、API资源层

FileController控制器作为API的提供者,接收用户的请求及响应。

API的定义符合RESTful的风格。

@CrossOrigin(origins = "*", maxAge = 3600) // 允许所有域名访问@Controllerpublic class FileController {@Autowiredprivate FileService fileService;@Value("${server.address}")private String serverAddress;@Value("${server.port}")private String serverPort;@RequestMapping(value = "/")public String index(Model model) {// 展示最新20条数据model.addAttribute("files", fileService.listFilesByPage(0, 20));return "index";}/*** 分页查询文件** @param pageIndex* @param pageSize* @return*/@GetMapping("files/{pageIndex}/{pageSize}")@ResponseBodypublic List&<File&> listFilesByPage(@PathVariable int pageIndex,@PathVariable int pageSize) {return fileService.listFilesByPage(pageIndex, pageSize);}/*** 获取文件片信息** @param id* @return*/@GetMapping("files/{id}")@ResponseBodypublic ResponseEntity&<Object&> serveFile(@PathVariable String id) {Optional&<File&> file = fileService.getFileById(id);if (file.isPresent()) {return ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION, "attachment; fileName=""+ file.get().getName() + """).header(HttpHeaders.CONTENT_TYPE, "application/octet-stream").header(HttpHeaders.CONTENT_LENGTH, file.get().getSize() + "").header("Connection", "close").body(file.get().getContent().getData());} else {return ResponseEntity.status(HttpStatus.NOT_FOUND).body("File was notfound");}}/*** 在线显示文件** @param id* @return*/@GetMapping("/view/{id}")@ResponseBodypublic ResponseEntity&<Object&> serveFileOnline(@PathVariable String id) {Optional&<File&> file = fileService.getFileById(id);if (file.isPresent()) {return ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION, "fileName=""+ file.get().getName() + """).header(HttpHeaders.CONTENT_TYPE, file.get().getContentType()).header(HttpHeaders.CONTENT_LENGTH, file.get().getSize() + "").header("Connection", "close").body(file.get().getContent().getData());} else {return ResponseEntity.status(HttpStatus.NOT_FOUND).body("File was notfound");}}/*** 上传** @param file* @param redirectAttributes* @return*/@PostMapping("/")public String handleFileUpload(@RequestParam("file") MultipartFile file,RedirectAttributes redirectAttributes) {try {File f = new File(file.getOriginalFilename(), file.getContentType(),file.getSize(), new Binary(file.getBytes()));f.setMd5(MD5Util.getMD5(file.getInputStream()));fileService.saveFile(f);} catch (IOException | NoSuchAlgorithmException ex) {ex.printStackTrace();redirectAttributes.addFlashAttribute("message", "Your "+ file.getOriginalFilename() + " is wrong!");return "redirect:/";}redirectAttributes.addFlashAttribute("message","You successfully uploaded " + file.getOriginalFilename() + "!");return "redirect:/";}/*** 上传接口** @param file* @return*/@PostMapping("/upload")@ResponseBodypublic ResponseEntity&<String&> handleFileUpload(@RequestParam("file") MultipartFilefile) {File returnFile = null;try {File f = new File(file.getOriginalFilename(), file.getContentType(), file.getSize(),new Binary(file.getBytes()));f.setMd5(MD5Util.getMD5(file.getInputStream()));returnFile = fileService.saveFile(f);String path = "//" + serverAddress + ":" + serverPort + "/view/" +returnFile.getId();return ResponseEntity.status(HttpStatus.OK).body(path);} catch (IOException | NoSuchAlgorithmException ex) {ex.printStackTrace();return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ex.getMessage());}}/*** 删除文件** @param id* @return*/@DeleteMapping("/{id}")@ResponseBodypublic ResponseEntity&<String&> deleteFile(@PathVariable String id) {try {fileService.removeFile(id);return ResponseEntity.status(HttpStatus.OK).body("DELETE Success!");} catch (Exception e) {return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(e.getMessage());}}}

其中@CrossOrigin(origins=&”*&”,maxAge=3600)注解标识了API可以被跨域请求。

运行

有多种方式可以运行Gradle的Java项目。使用Spring Boot GradlePlugin插件运行是较为简便的一种方式,只需要执行:

$ gradlew bootRun

项目成功运行后,通过浏览器访问http://localhost:8081即可。如图14-4所示,首页提供了上传的演示界面,上传后,就能看到上传文件的详细信息。

图14-4 上传界面

其他配置项

我们打开application.properties配置文件,可以看到以下配置。

server.address=localhostserver.port=8081# Thymeleafspring.thymeleaf.encoding=UTF-8spring.thymeleaf.cache=falsespring.thymeleaf.mode=HTML5# limit upload file sizespring.http.multipart.max-file-size=1024KBspring.http.multipart.max-request-size=1024KB# independent MongoDB server#spring.data.mongodb.uri=mongodb://localhost:27017/test

这些配置的含义如下。

·server.address和server.port用来指定文件服务器启动的位置和端口号。

·
spring.http.multipart.max-file-size和spring.http.multipart.max-request-size用来限制上传文件的大小,这里设置最大是1MB。

·当spring.data.mongodb.uri没有被指定的时候,默认会采用内嵌MongoDB服务器。如果要使用独立部署的MongoDB服务器,那么设置这个配置,并指定MongoDB服务器的地址。同时,将内嵌MongoDB的依赖注释掉,操作如下。

dependencies {//...// 注释掉内嵌的 MongoDB// compile('de.flapdoodle.embed:de.flapdoodle.embed.mongo')//...}

本文给大家讲解的内容是分布式系统开发实战: 分布式存储,实战:基于MongoDB文件服务器

“mongodb分布式部署(mongodb三种部署方式)” 的相关文章

如何注册网站域名(手把手教你注册网站域名)

“域名”是Internet的中央服务。作为可以将域名和IP地址相互映射的分布式数据库,它使人们可以更轻松地访问Internet,而不必记住可以直接由机器读取的I...

上市公司独董离职潮苗头初显(但还没有那么夸张)

又有多家上市公司出现独立董事辞职。一晚上5家A股公司独董辞职截至晚间发稿,11月19日A股共有5家上市公司发布独董辞职公告,包括金花股份(600080)、科新发...

大疆无人机御pro使用教程(这款小型无人机外观精致续航

近年来小型无人机逐渐走进了消费级市场,如零度智控、Yuneec、曼塔智能S6等掌上无人机都获得了不错的市场反馈。这样小巧轻便的无人机降低了玩家的操作门槛,让更多...

分布式存储软件功能(ceph分布式存储优缺点)

Ceph简介Ceph存储集群至少需要1个CephMonitor和2个OSD守护进程。运行Ceph文件系统客户端时,则必须要有元数据服务器(MetadataSer...

人工智能企业有哪些(中国智能机器人企业排名)

如果说有一项技术彻底改变了21世纪,那一定是人工智能。Google新掌门人SundarPichai也曾说:“人工智能带给我们生活和工作的改变,甚至将超过火和电。...

10万元以下车型推荐,这四款车既省油价格又实惠

即便如今国内汽车消费水平提高了,但是,走量车型的预算范围依旧在10万块左右的车型,甚至10万块以下的车型为主。这是因为普通家庭的汽车消费水平,依旧还停留在这样一...

二寸照片的尺寸是多少(1寸和2寸照片大小对比)

【提醒】过年啦!这三样东西不要随便借出!当心麻烦找上来来源:人民日报春节了,亲朋好友团聚一起,难免出现互相帮助的时候,但值得注意的是,有些东西是不可以随便借给他...

iphone6s屏幕尺寸多大(苹果手机各个型号屏幕尺寸)

iPhone6s一直都是备受热议的对象,近期更是传闻该机屏幕分辨率有望达到1080p。不过,如今IHSTechnology中国研究总监王阳在微博上的发言否定了上...

大学生怎么创业比较好,大学生创业50个小案例

现在,越来越多的大学生都投入到创业这一个可以缓解就业压力的途径。可是,大多数的大学创业者都是仅有一颗创业心,却不知道自己要往哪方面去努力。其实,大学生创业说简单...

公园地摊什么最好卖,适合公园摆摊的3的新鲜项目

地摊经济是当今社会的一个热门词汇,在摆地摊开始流行后,许多人纷纷走上街去开始自己的地摊生涯。这摆地摊也是有学问的,那么什么更适合在地摊上卖呢?下面就来聊聊我自己...