欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2023-11-29 21:25:34
实战:基于mongodb文件服务器本节,我们将介绍如何基于mongodb技术来存储二进制文件,从而实现一个文件服务器mongodb file server。文件服务器的需求本文件服务器致力于小型文件的...

实战:基于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:${springboot
version}")
}
}
// 使用插件
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;
...
@document
public 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;
}
@override
public 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);
}
@override
public int hashcode() {
return java.util.objects.hash(name, contenttype, size, uploaddate, md5, id);
}
@override
public 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中所有的接口。
@service
public class fileserviceimpl implements fileservice {
@autowired
public filerepository filerepository;
@override
public file savefile(file file) {
return filerepository.save(file);
}
@override
public void removefile(string id) {
filerepository.deletebyid(id);
}
@override
public optional<file> getfilebyid(string id) {
return filerepository.findbyid(id);
}
@override
public 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) // 允许所有域名访问
@controller
public class filecontroller {
@autowired
private 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}")
@responsebody
public list<file> listfilesbypage(@pathvariable int pageindex,
@pathvariable int pagesize) {
return fileservice.listfilesbypage(pageindex, pagesize);
}
/**
* 获取文件片信息
*
* @param id
* @return
*/
@getmapping("files/{id}")
@responsebody
public 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 not
found");
}
}
/**
* 在线显示文件
*
* @param id
* @return
*/
@getmapping("/view/{id}")
@responsebody
public 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 not
found");
}
}
/**
* 上传
*
* @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")
@responsebody
public responseentity<string> handlefileupload(@requestparam("file") multipartfile
file) {
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}")
@responsebody
public 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所示,首页提供了上传的演示界面,上传后,就能看到上传文件的详细信息。

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

图14-4 上传界面

其他配置项

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

server.address=localhost
server.port=8081
# thymeleaf
spring.thymeleaf.encoding=utf-8
spring.thymeleaf.cache=false
spring.thymeleaf.mode=html5
# limit upload file size
spring.http.multipart.max-file-size=1024kb
spring.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文件服务器