前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SpringCloud微服务架构实战使用分布式文件系统DFS

SpringCloud微服务架构实战使用分布式文件系统DFS

作者头像
愿天堂没有BUG
发布2022-10-28 16:35:25
5040
发布2022-10-28 16:35:25
举报
文章被收录于专栏:愿天堂没有BUG(公众号同名)

使用分布式文件系统 DFS

微服务应用使用分布式方式进行部署,并且有可能随时随地部署多个副本,所以必须有一个独立的文件系统来管理用户上传和使用的资源文件,包括图片和视频等。

在模块goods-web 的设计中,我们是使用FastDFS这个轻量级的分布式文件系统来设计的。如果使用云服务商提供的对象存储服务来设计,如OSS服务等,则可以参照服务提供商的使用说明,并结合本实例进行设计。

下面针对库存管理中,商品创建和编辑时使用的图片,实现在FastDFS上进行存储和管理的设计。

有关FastDFS的安装、集群构建和相关配置等,将在运维部署部分的相关章节中进行介绍。

分布式文件系统客户端开发

FastDFS 提供了Java语言使用的客户端开发包,但在Spring Boot中使用时还需要进行二次开发。为了简化开发过程,我们使用tobato在GitHub上开源的一个专为Spring Boot开发者提供的封装。

首先,在goods-web模块中,增加如下依赖引用:

代码语言:javascript
复制
<dependency>
<groupId>com.github.tobato</groupId><artifactId>fastdfs-client</artifactId><version>1.26.4-RELEASE</version>
</dependency>

然后,在模块的配置文件application.yml 中增加如下配置:

代码语言:javascript
复制
fdfs:
soTimeout: 1501
connectTimeout:601thumbImage:
width: 150height: 150trackerList:
- 192.168.1.214:22122-192.168.1.215:22122spring.jmx.enabled: false
file.path.head:http://192.168.1.214:8080/

这个配置假设 FastDFS 的TrackerServer安装了两台服务器,它们的P地址分别为“192.168.1.214”和“192.168.1.215”,并且可以通过链接“http:/192.168.1.214:8080/”使用文件。

接着,在工程的启动文件中增加注解@Import和@EnableMBeanExport,即导入fastdfs-client的相关配置,代码如下所示:

代码语言:javascript
复制
@SpringBootApplication@EnableDiscoveryClient
@EnableFeignClients (basePackages = "com.demo")@componentScan(basePackages = "com.demo")
R Import(FdfsClientConfig.class)
@EnableMBeanExport(registration = RegistrationPolicy.IGNORE_EXISTING)public class GoodswebApplication {
public static void main(String[]args) {
SpringApplication.run (GoodsWebApplication.class, args);
}
}

为了确认上面的引用和配置都已经准备就绪,可以启动应用验证一下。如果启动应用正常,则说明上面的配置是正确的。

现在,我们就可以创建一个“FastefsClient”实现文件的上传功能了,代码如下所示:

代码语言:javascript
复制
@service
public class FastefsClient {
@Autowired
protected FastFileStorageClient storageClient;
public String uploFile (MultipartFile file){
String fileType=
FilenameUtils.getExtension(file.get0riginalFilename ()).toLowerCase();
StorePath path =null;
try {
path = storageclient.uploadFile(file.getInputstream(),
file.getsize(, fileType, nul1);
Jcatch (IOException e){
e.printstackTrace();
}
if(path !=null) {
return path.getFu1lPath(;}else {
return null;
}
public string uploile(Inputstream inputstream,Long size, String type)(
StorePath path = null;
try {
path = storageclient.uploadFile(inputStream, size, type, null);}catch(Exception e){
e.printStackTrace();
}
if(path!=null) {
return path. getFullPath();}else i
return null;
public boolean deleteFile(string fullPath)
try {
storageClient.deleteFile(fullPath);return true;
}catch(Exception e){
e-printstackTrace(;
}
return false;
}
}

这里,设计了一个多态的uploFile方法,可以使用不同的参数通过调用FastFileStorageClient实现文件上传,同时设计了一个deleteFile方法,实现文件的删除操作。

商品图片上传设计

商品图片上传步骤如下。

首先,设计一个控制器PicUtilController;然后,在这个控制器中实现文件上传的功能,代码如下所示:

代码语言:javascript
复制
@Controller
@RequestMapping("/pic")
public class PicUtilController {
@value("${file.path.head:http://192.168.1.214:84/}")private String pathHead;
GAutowired
private FastefsClient fastefsClient;
//可缩放图片上传
CRequestMapping ("/upload")public String upload() {
return "pic/upload-pic";
}
/*★
*上传图片* @return大/
@RequestMapping(value = "/uploadPic", method =RequestMethod. POST)
public void uploadPic(@RequestParam ("pictureFile") MultipartFile
multipartFile,HttpServletRequest request,HttpServletResponse response)
try{
String filename = fastefsclient.uploFile (multipartFile);Long shopid =1L;
AsyncThreadPool.getInstance().execute(new Runnable()(
Goverride
public void run(){
try{
savePic(multipartFile, filename ,shopid);]catch (Exception e){
e.printStackTrace();
});
BufferedImage image = ImageIO.read(multipartFile.getInputStream());
Map<String,0bject> data = new HashMap<String,0bject>();
data.put("pathInfo", pathHead+filename);
data.put("width", image.getWidth());
data.put ("height", image.getHeight());
ObjectMapper mapper = new ObjectMapper();
String ret = mapper.writeValueAsString(data);
response.setContentType("text/html; charset=utf8");response.getOutputStream().write(ret. getBytes());response.flushBuffer();
}catch (IOException e){
e.printStackTrace();
}
}
...
}

这个控制器设计了一个链接“/upload”,用来打开上传文件的操作界面。另外,另一个链接“/uploadPic”通过调用前面设计的文件客户端“FastefsClient”实现文件上传。上传后再将图片的路径和文件大小等信息返回给调用者。

在上面的代码中,文件上传的操作界面在视图设计“upload-pic.html”(源代码文件代码行30~33行)中实现,主要使用一个“input”控件从操作者的机器上选取文件进行上传,代码如下所示:

代码语言:javascript
复制
<div class="upload-box">
//点击上传
<input id="pictureFile" name="pictureFile" type="file" class="file"onchange="uploadPic submit (this)"/>
</div>

通过“upload-page.,js”(源代码文件代码行432~437行)设计一个文件上传的请求方法,即可在视图界面上调用上面的控制器设计中上传文件的链接“/uploadPic”了。调用成功后再取出文件信息,代码如下所示:

代码语言:javascript
复制
//上传图片
function ajaxFileUpload(id){
var url = '/pic/uploadPic';$.ajaxFileUpload({
url :url,//需要链接到服务器地址
fileElementId : id, //文件选择框的id属性
dataType : 'json',//服务器返回的格式,可以是jsonsuccess:function(data){
if(data.errorMsg){
showMsg(data.errorMsg,"错误");}else{
page.upload.finish(data.pathInfo, data.width, data.height);
}
}
}};
}

通过上述方法获取到文件信息之后,再通过“upload-pic.html”(源代码文件代码行53~80行)视图展现出来,这部分的设计代码如下所示:

代码语言:javascript
复制
<div class="img-select">
<div class="up-tit">选择图片后可以在下框中调整您所需的部分。</div><div class=" operateBox">
<span>上传图片后预览</span>
<div class=" operate" id="operate">
<img/>
<div class="isee">
<div class="b top"></div>
<div class="b_right"></div><div class="bbottom" ></div><div class="b left"></div>
<div class="handle h_top_left"></div><div class="handle h top"></div>
<div class="handle h_top right"></div><div class="handle h_right"></div>
<div class="handle hbottom right"></div><div class="handle h_bottom"></div>
<div class="handle h bottom_left"></div><div class="handle h_left"></div>
<div class="s_c"></div>
</div>
<div class="o_bg bg_top"></div><div class="o_bg bg_right"></div><div class="o_bg bg_bottom"></div><div class="o_bg bg_left"></div></div>
</div></div>

上面的设计完成后,最后显示的效果如图7-5所示。

其中,在进行图片选取时,还可以对图片进行裁剪,有关这部分的功能请查看项目的源代码。

注意,在进行上面的整个调试时,必须保证有分布式文件系统服务可以访问。

富文本编辑器上传图片设计

在库存管理中,对商品内容的编辑建议使用富文本编辑器,这样可以编辑出图文并茂的内容。使用富文本编辑器上传图片的原理与7.7.2节中的图片上传的设计基本相同。

这里以使用开源的ueditor富文本编辑器为例进行说明。

代码语言:javascript
复制
在控制器PicUtilController的设计中,使用如下所示的uploadimg方法设计:
@controller
@RequestMapping("/pic")
public class PicUtilController {
evalue("${file.path.head:http://192.168.1.214:84/}")private String pathHead;
@Autowired
private FastefsClient fastefsClient;
//ueditor 图片上传
@RequestMapping (value = "/uploadimg", method= RequestMethod. POST,produces="text /html; charset=UTE-8")
public void uploadimg(CRequestParam ( "upfile") MultipartFile upfile,HttpServletRequest request,HttpServletResponse response){
try {
String filename= fastefsclient. uploFile(upfile);
Long shopid =1L;
AsyncThreadPool.getInstance() .execute(new Runnable(){
coverride .
public void run() {
try {
savePic(upfile,filename , shopid);}catch(Exception e){
e-printStackTrace();
}
}
}};
Map<String,0bject> data = new HashMap<String,0bject>();data.put ("original",upfile.getOriginalFilename());
data.put("url",pathHead+filename);
data.put("title","");
data.put ("state", "sUCCESS");
ObjectMapper mapper =new 0bjectMapper();
String ret = mapper.writevalueAsString (data);
response.setContentType ("text/html; charset=utf8");response.getOutputStream().write(ret.getBytes());response.flushBuffer(;
}catch (Exception e){
e.printStackTrace();
}
}
}

与商品图片上传设计不同的地方是返回参数有点不一样,主要是根据ueditor插件的需要进行调整和设定。

在新增商品new.html和编辑商品edit.html的页面上增加如下所示代码,引用ueditor插件:

代码语言:javascript
复制
<script type="text/javascript" charset="utf-8">
window.UEDITOR_HOME_ URL = "/ueditor/";</script>
<script type="text/javascript" charset="utf-8"th:src="@{/ueditor/editor_config.js} "></script>
<script type="text/javascript" charset="utf-8"th:src="@{/ueditor/editor_all.js} "></script>
<script type="text/javascript">
var ME=UE .getEditor('contents',
{
wordCount:false,
initialFramewidth: 406,maximumwords:50000,
wordoverFlOwMsg:'最多输入50000个字符,toolbars:[
[ ' fullscreen', 'undo', 'redo', 'l','bold','underline',
'strikethrough', 'superscript', 'subscript', 'removeformat',
'formatmatch', 'autotypeset', 'I', 'forecolor',
'backcolor', 'cleardoc', 'T','rowspacingtop', 'rowspacingbottom ' ,
' lineheight','I','justifyleft', 'justifycenter',
'justifyright', 'justifyjustify','I', 'imagenone', 'imageleft',
' imageright', 'imagecenter','l','customstyle',
'paragraph', 'fontsize','l', 'emotion', 'date', 'time', 'spechars',
'L','searchreplace', 'insertimage', 'wordimage',
'link ']
]});
</script>

最后,必须在ueditor 插件的配置文件“editor_config.js”(源代码文件代码行38~41行)中,更改如下所示的几行配置:

代码语言:javascript
复制
//图片上传配置区
, imageUrl: " /pic/uploadimg"//图片上传提交地址
, imagePath:""//图片修正地址,引用了fixedImagePath,如有特殊需求,可自行配置
,imageFieldName: "upfile"//图片数据的 key,若此处修改,需要在后台对应文件修改对应参数

这样,就可以使用富文本编辑器上传图片文件了。

设计完成后,显示的效果如图7-6所示。

建立本地文件信息库

当一个文件上传之后,为了方便以后可以继续使用这个文件,我们可以在本地建立一个文件信息库,用来保存一个文件的简要信息。实现方法如下。

首先,有关数据服务的设计部分在模块goods-restapi 中。本地信息的图片数据对象:

代码语言:javascript
复制
@Table(name = "t picture")@Data
public class Picture{
@Id
@Generatedvalue(strategy =GenerationType.IDENTITY)private Long id;//图片编号
@column(name = "path info")
private String pathInfo;//文件路径Ccolumn(name = "file name")
private String fileName;//文件名private int width;//宽度
private int height;//高度private String flag;//标志
@DateTimeFormat (pattern = "yyyy-MM-dd HH:mm:ss")
ecolumn (name = "created",columnDefinition = "timestamp defaultcurrent_timestamp")
@Temporal (TemporalType.TIMESTAMP)private Date created;//创建时间private Long merchantid;//商家编号
}

对应的数据库表结构t _picture的定义:

代码语言:javascript
复制
CREATE TABLE 'tpicture'(
'id' bigint (20) NOT NULL AUTO_INCREMENT,
'created'timestamp NOT NULL DEFAULT CURRENT TIMESTAMP,'file_name' varchar(255)COLLATE utf8 bin DEFAULT NULL,'flag'varchar(255)COLLATE utf8 bin DEFAULT NULL,
'height' int(11) DEFAULT NULL,
'merchantid' bigint (20) DEFAULT NULL,
'path info' varchar(255)COLLATE utf8_bin DEEAULT NULL,'width' int(11) DEFAULT NULL,
PRIMARY KEY ('id')
ENGINE=InnoDB AUTO INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

有关本地图片信息库的数据服务部分的实现,可以参照商品数据服务的设计方法,不再赘述。成功上传文件之后,如何将文件的路径和简要信息保存到本地文件信息库中呢?

文件信息保存在控制器PicUtilController 的savePic方法中,代码如下所示:

代码语言:javascript
复制
@controller
@RequestMapping("/pic")
public class PicUtilController {
Value("${file.path.head:http://192.168.1.214:84/}")private string pathHead;
CAutowired
private PictureRestService pictureRestService;
private String savePic(MultipartFile multipartFile,String filename,Longshopid) throws Exception{
BufferedImage image = ImageI0.read (multipartFile.getInputStream ());
PictureQo picture= new PictureQ0();
picture.setFileName (filename);
picture.setHeight (image. getHeight ());picture.setWidth(image.getwidth());picture.setPathInfo(pathHead);
picture.setMerchantid(shopid);
return pictureRestService.create(picture);
}
}

在使用了本地文件信息库之后,在库存管理中创建和编辑商品时就可以使用已经上传的文件了。

为了能够更好地使用本地文件信息库,我们需要在控制器PicUtilController中创建一个分页查询本地文件信息库的方法 listPic,代码如下所示:

代码语言:javascript
复制
@controller
@RequestMapping("/pic")
public class PicUtilController{
@value("${file.path.head:http://192.168.1.214:84/}")private String pathHead;
@Autowired
private PictureRestService pictureRestService;
@RequestMapping(value = "/listPic",method = RequestMethod.POST)
@ResponseBody
public Page<Map<String,0bject>>listPic(PictureQo pictureQo){
String json = pictureRestService.findPage (pictureQo);
Gson gson = TreeMapConvert.getGson();
TreeMap<String, 0bject> page = gson.fromJson(json,new TypeToken<
TreeMap<String, Object>>(){}.getType());
Pageable pageable = PageRequest.of(pictureQo.getPage(),
pictureQo.getSize( , null);
java.util.List<PictureQ0> list = new ArrayList<>();
if(page != null && page.get ("list") !=null) {
list = gson.fromJson (page.get ("list").toString(), new
TypeToken<List<PictureQ0>>() {
}.getType());
String count - page.get( "total").toString();
return new PageImpl(list, pageable,new Long ( count));
}
}

其中,控制器使用链接“/listPic”为页面设计提供调用方法,返回本地文件信息的分页列表。

在页面上使用脚本定义upload-page.,js(源代码文件代码行501~525行),实现文件信息库分页查询结果的处理方法,代码如下所示:

代码语言:javascript
复制
function getDataHtml (pageNo, pagesize) {
$.ajax(i
url: "/pic/listPic",dataType: "json",type: "POST",
cache: false,
data: {page: pageNo-1,size: pagesize ll 8},success:function (data){
var $list =$('#upload-list').empty();$.each (data.content, function (i, v) {
var html ="";
html +='<div class="upload-item">'+
'<div class="img"><img src="'+ v.pathInfo + v.fileName +
"/></div>'+
'<p>'+v.width+'x'+ v.height+'</p>'+'<div class="selected"></div>'+
'</div>';
$list.append($ (html));
})
page.photos.setPosition(;
document.getElementById('pagebar').innerHTML=
PageBarNumList.getPageBar (data.number+1, data.totalPages,3,'getDataHtml',pagesize ll8,true);
},
errOr: function (e) {}});
}

设计完成之后,展示的效果如图7-7所示。

总体测试

在库存管理项目整体开发完成之后,可以进行一个总体测试。这个测试需要调用类目管理的微服务接口,所以在进行测试时,可按下列顺序启动各个模块。

(1)启动类目接口服务:catalog-restapi模块。

(2)启动库存管理微服务API应用:goods-restapi模块。

(3)启动库存管理PC端Web应用:goods-web模块。

上面几个模块启动成功之后,可在浏览器打开如下链接地址:

代码语言:javascript
复制
http://localhost:8092

如果各个模块都能正常运行,则可以在库存管理首页中显示已有的商品列表,如图7-8所示。

在这个页面中,我们可以新增或者编辑商品。编辑商品的操作界面如图7-9所示。

小结

本章介绍了库存管理的微服务接口和一个相关的Web应用微服务的开发。在这个项目的开发过程中,我们使用了半自动的数据库开发框架MyBatis,体验了与使用JPA不同的开发实践。在生产应用中,读者可以根据实际情况选择使用。

同时,本章的Web应用开发也演示了使用分布式文件系统的方法,不管是使用DFS,还是使用OSS,其设计思路和实现方法基本一致,所以我们只需掌握一种开发方法,就能够在实际应用中应用自如。

本文给大家讲解的内容

SpringCloud微服务架构实战:库存管理与分布式文件系统,使用分布式文件系统DFS、总体测试

  1. 下篇文章给大家讲解的是SpringCloud微服务架构实战:海量订单系统微服务开发;
  2. 觉得文章不错的朋友可以转发此文关注小编;
  3. 感谢大家的支持!

本文就是愿天堂没有BUG给大家分享的内容,大家有收获的话可以分享下,想学习更多的话可以到微信公众号里找我,我等你哦。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-04-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 愿天堂没有BUG 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 使用分布式文件系统 DFS
  • 总体测试
  • 小结
  • 本文给大家讲解的内容
相关产品与服务
云 HDFS
云 HDFS(Cloud HDFS,CHDFS)为您提供标准 HDFS 访问协议,您无需更改现有代码,即可使用高可用、高可靠、多维度安全、分层命名空间的分布式文件系统。 只需几分钟,您就可以在云端创建和挂载 CHDFS,来实现您大数据存储需求。随着业务需求的变化,您可以实时扩展或缩减存储资源,CHDFS 存储空间无上限,满足您海量大数据存储与分析业务需求。此外,通过 CHDFS,您可以实现计算与存储分离,极大发挥计算资源灵活性,同时实现存储数据永久保存,降低您大数据分析资源成本。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档