同学们好!成功登录《我家租房网》系统的用户,可以点击位于搜索页面右上角的用户名,进入用户页面。点击用户页面中的“我的房源”,会打开我的房源页面。点击我的房源页面中的“发布新房源”,会打开发布新房源页面。用户在填写完房源信息表单后,点击“发布房源信息”,会打开上传房屋图片页面。点击上传房屋图片页面中的“选择文件”,选择事先准备好的房屋图片后,点击“上传”,系统后台会将房屋图片保存到文件服务器,同时将文件服务器返回的文件凭证存入数据库。这节课我们将实现上传房屋图片的功能。


我们将首先实现支持上传图片的后端微服务,再在前端服务器中添加路由处理函数并远程调用后端微服务,最后再将前后端连在一起做完整的功能测试。


首先我们来实现后端服务器。


第一步是创建特定的微服务。


创建微服务的过程还是和先前一样,直接在back-end工程目录下执行go-micro命令,子命令为new,参数为service,微服务名为UploadImage。


接着,需要修改一下go-micro为我们自动生成的接口描述。在back-end工程目录下的UploadImage微服务目录中,有一个名为proto的子目录,这就是存放接口描述脚本及其Go代码文件的地方。现阶段里面只有一个名为UploadImage.proto的ProtoBuf脚本文件。我们需要对该文件做一些修改:

这段代码描述的是,前端服务器和后端微服务之间的数据交换格式。CallRequest表示前端提供给后端的请求数据,其中包含字符串形式的房屋ID、字节序列形式的图片数据,和字符串形式的图片文件名后缀。CallResponse表示后端返回给前端的响应数据,其中包含错误代码、错误描述和图片信息。图片信息中只含有一个字段,即房屋图片的URL。


有了用ProtoBuf语言描述的远程调用接口,我们还需要把它编译成基于Go语言的程序代码。在UploadImage微服务目录中执行make命令,并携带四个目标参数,它们是init、proto、update和tidy。


在UploadImage微服务目录下,创建一个名为model的子目录,表示该微服务的模型层。在该子目录中创建mysql.go文件,封装所有与操作MySQL数据库有关的代码。在实际编写数据库访问代码之前,我们可以先把之前为前端服务器编写的mysql.go文件中的内容原封不动地复制到这里:


修改UploadImage微服务目录中的main.go文件,添加对InitDB函数的调用,将服务发现改为Consul,绑定IP地址和端口:

程序启动伊始,即完成对数据库的初始化。其中包括两个动作,其一是连接ihomedb数据库并获得连接池对象,其二是在该数据库中创建表。当然,如果这些表已经存在,则不再创建。接着,在服务器对象的初始化部分,指示其注册到Consul服务器,同时绑定本机的9011端口。


接下来,我们为UploadImage微服务编写与数据持久化有关的代码。


打开UploadImage微服务目录下,model子目录中的mysql.go文件,在其中添加有关更新MySQL数据库中的图片的代码:

定义名为UpdateImage的函数,用于更新MySQL数据库中的图片。该函数接收房屋ID和该房屋的图片文件凭证两个参数。以房屋ID为条件,查询房屋表中特定记录,获得表示该记录的房屋结构体变量。检查该结构体变量中表示主图片URL的成员,若其值为空,则以从参数传入的房屋图片文件凭证,更新该房屋记录的主图片URL字段。否则创建并初始化一个表示图片表记录的结构体变量,将房屋ID和该房屋的图片文件凭证填入其中,通过数据库连接池对象的Create方法,将该结构体变量所表示的图片记录插入到图片表中,返回错误对象。这里需要强调的是,一座房屋可能会有多张图片,其中一张为主图,其文件凭证保存在房屋表的主图片URL字段中,其它为副图,保存在图片表的图片URL字段中,并通过该表的所属房屋ID字段,与房屋表中的特定房屋建立关联。


在完成UploadImage微服务模型层的所有开发工作之后,我们将着手编写用于处理上传图片业务的代码。


打开UploadImage微服务目录下,handler子目录中的UploadImage.go文件,在其中的Call方法里,添加与上传图片有关的操作:

这里首先以FastDFS客户机配置文件的路径为参数,调用FastDFS客户端SDK的NewClientWithConfig函数,创建一个FastDFS客户机对象。接着将请求对象中的图片数据和图片文件名后缀,传给FastDFS客户机对象的UploadByBuffer方法。注意调用该方法的第二个参数不包括文件名后缀中的“.”字符。如果不发生错误的话,房屋的图片文件已经上传到FastDFS服务器中了。FastDFS客户机对象的UploadByBuffer方法成功返回字符串形式的图片文件凭证。我们以请求对象中的房屋ID和上一步得到的图片文件凭证为参数,调用模型层的UpdateImage函数,更新MySQL数据库中的图片。若成功,则将图片文件凭证拼接在Nginx服务器的IP地址和侦听端口之后,形成供前端下载房屋图片的URL,填入响应对象的特定字段中。


在完成UploadImage后端服务器的开发后,我们需要为前端服务器添加一条路由,并在路由处理函数中完成对后端微服务远程方法的调用。


前端和后端共用同一套接口描述。


因此,这里我们将UploadImage微服务目录下proto子目录中的所有文件,原封不动地复制到front-end工程目录proto子目录下的UploadImage目录中。


这里我们要为上传图片添加一条路由,POST方法结合/api/v1.0/houses/:id/images路径,处理函数名为UploadImage。


在front-end工程目录下的main.go文件中添加一条路由:

这里我们调用了路由对象的POST方法,为发布房源添加了一条路由,将POST方法结合/houses/:id/images路径,路由到controller包的UploadImage函数。将UploadImage函数定义在controller包里是因为该函数的主要任务是执行业务逻辑,属于MVC中的C,即控制器层的部分。


当然,在controller包里真的得有UploadImage函数。为此,我们打开front-end工程目录下controller子目录中的house.go文件。在该文件的import部分增加一行,同时定义UploadImage函数:

这段代码首先调用了Gin框架调用此函数时传入的上下文对象的Param方法,取添加路由时放在请求路径中的占位符“id”为参数,接收其返回的房屋ID。接着从请求包体中根据键获取图片文件头对象,并通过该对象的Open方法获得图片文件对象。根据图片文件的大小分配足量的字节切片,将图片文件中的数据读到该切片中。通过Consul服务发现,获取有关名为uploadimage的微服务的信息,远程调用其中的UploadImage对象的Call方法。该方法以包含房屋ID、图片数据和图片文件名后缀的请求对象为参数,并返回包含错误代码、错误描述和图片信息的响应对象。图片信息中包含房屋图片的URL,浏览器将据此显示房屋图片。最后将该响应对象序列化为一个JSON字符串,编码到HTTP响应中,回传给浏览器。


至此,我们已经完成上传图片的全部开发工作。下面我们将对这部分功能进行测试。


在虚拟机中启动FastDFS和Nginx。启动Consul服务器、GetAreas、UserLogin、GetUser、PublishHouse、UploadImage后端微服务和前端服务器。通过登录页面登录系统,点击位于搜索页面右上角的用户名,进入用户页面,点击“我的房源”,进入我的房源页面,点击“发布新房源”,进入发布新房源页面,填写房源信息表单,点击“发布房源信息”,进入上传房屋图片页面,点击“选择文件”,选择图片文件并点击“上传”。可上传多张房屋图片。浏览器会显示所上传的全部房屋图片。


进入MySQL控制台,检查ihomedb数据库houses表和images表中与房屋图片有关的字段。


谢谢大家,我们下节课再见!