🔧 技术栈
后端
- Java 8
- 开发框架:SpringBoot 2.x
- 数据访问:MyBatis Plus
- 项目管理:Maven
- 接口文档:Knife4j
存储
- 数据库:MySQL(微信云托管平台)
- 对象存储:Tencent COS
- CDN:Tencent CDN
项目进度
数据库搭建(远程开通)
微信云托管:https://cloud.weixin.qq.com/cloudrun
作用:可以帮助用户快速地托管后端的服务,可以快速地托管项目,帮助用户运维开发,提供数据库之类的服务
但是,这东西是要钱的
现在已经让微信云托管配合腾讯云帮助我们初始化好了数据库,并且现在我们可以可视化地在任何电脑上对数据库进行管理
现成的云服务会比本地的数据库方便很多
搭建基础demo,整合框架
jar包:直接运行
war包:配合Tomcat等服务器进行访问
Lombok:团队中最好不要使用
原因:在企业的团队开发中,如果有队友、同事用的编辑器跟你的不一样,那他可能会报错;再或者没装插件、或者你在服务器上安装,在服务器上编译也有可能出问题(主要就是为了统一团队的开发)
搭建好后运行一下
数据库没有指定数据源
原因:因为我们引入了数据库的驱动,所以必须要指定这东西
不管了,一顿操作猛如虎,先把它的架构搭建起来
删掉:
因为这些都是前后端项目一体化的遗留物
启动web项目,可以在网页中访问
新建一个HealthController(心跳检测Controller):用来检测我们的项目能否成功运行
然后改一下配置文件(properties的语法稍微麻烦,就用yml)
接下来还没法运行,因为上面那个数据库的报错还没解决
我们可以先把数据库关掉(一个防止数据库注入的注解)
拿报错一搜就出来了https://blog.csdn.net/qq_45498460/article/details/112860809
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
没有访问路径的话它默认是根路径了
我们也可以自己去配置它的访问路径
加一个后端的标识,再去把心跳检测的路径改为health
要用requestMapping来指定路径
整合数据库
这一步呢,我们去配置一下数据库的配置就可以了,如果是在企业中开发,最好是要去用一个连接池
这里我们可以使用Druid,我们先去GitHub上找一下配置,搜索Druid spring boot starter
加上我们的URL,用户名和密码(URL记得加上MySQL协议名)
接下来我们将屏蔽MySQL的注解去掉,跑一下
引入Mybatis-Plus框架,实现数据库的增删改查
首先我们先去一下它的官网,他这东西可以很方便地简化开发
它是一个数据访问层的二次封装的框架
我们直接快速开始
最新版本是3.4.3.4
使用就跟着官网来
它其实就是帮你扫描出来mybatis-plus帮助你生成的mapper
-
编写实体类
先搞一下库表,在通过逆向工程自动生成实体类,具体操作步骤看官方文档
编写Controller类
接下来创建controller类,打上注解
写上增删改查
首先写插入表情
先判断一下条件,如果为空就返回个-1以示尊敬
再return一个emojiService.save方法
这里我们注意到一个问题,就是它这个save方法它返回的是一个布尔类型
我们看一眼官网有没有提供返回其他类型的方法
、
可见全部都是返回的布尔值
所以我们可以当它成功插入到数据库后,我们就直接返回它的id,看它有没有被赋值为在数据库中新插入的数据主键值
然后是更新表情
我们依然是判断一下,如果emoji是空或者getid他两之间有一个是空的话吗,就返回一个-1
然后我们还是调用现成的方法啊
看它返回的类型。还是布尔值
update返回一个布尔值也是合情合理的,所有我们干脆都返回布尔值吧
然后接下来写搜索表情
搜索表情我们可以允许它搜索出来返回值是空,所以我们就不需要判断它是否为空了
我们先看看官网有没有分页查询的方法
下面两个是map的啊,那我们就用第二个吧
它这个方法首先接收一个page参数,那就只能new 一个page
看看page接收哪些值啊
ceurrent:就是当前是第几页
size:每页有多少条
后面就是我们的查询参数
我们看看能不能写一个queryWrapper
把emoji这个实体类注入进来 return 一个Ipage吧
最后再加一个GetMapping
这里我们再加一个条件判断
如果前端传来的它不是null,而是一个空字符串,那我们直接把emoji.setName设置为空,因为如果我们不去处理这个空字符串的话,它也会把这个空字符串塞到这个查询条件里的,这样就什么都查不出来了
而且还有一个点要提一下
emoji.getName().equals("")
这是我之前很容易犯的一个很经典的错误,这个我们要把它反过来
因为这个emoji.getName()这个属性它有可能为null,如果它为null的话,他是没有equals方法的,equits方法是Object对象才会有的方法 。也就是说,当它为null的时候,它没有equits方法,它就会直接报错
最后是删除表情
这里我们可以引入一个逻辑删除的插件,避免我们在采取removebyid的时候真实地把这条数据给删除了。
我们可以来看看数据库
这个isdelete字段就是判断我们的这条数据是否是有效的,0表示这条数据是有效的,1表示这条数据是无效的。而我们在删除数据时,我们只需要把这个0改成1就可以了
还是去官网看看怎么引入mybatis-plus的逻辑删除插件
在application.yml 这个springboot项目的全局配置里,去引入插件的配置
来都来了,顺便有一个问题解决一下,
这里我们漏加了个 /father 顺带加上
第二步加上 注解表示逻辑删除
找到我们的实体类
这里我们已经引入了
现在我们逻辑删除就完成了。这个逻辑删除插件安装之后,mybatis-plus就会自动帮我们把这个removebyid从真实删除改为逻辑删除,
把isdelete从0改为1
最后别忘了加上postmapping
其实除了查询之外,其他的都可以用PostMapping,问题不大
然后我们启动一下项目,这个时候我们只能调用getMapping查询,因为post请求没有办法在浏览器的地址里面直接测,所以我们来试一下
这个时候 会发现total为0,这里就出了点问题
我们去试着去官网找一个分页插件
最新版的使用了一个拦截器
我们就来写一个config,让springboot mybatis-plus starter自动注入
复制粘贴一下,把旧版删了
H2记得改成MYSQL
再运行一次
明显发现,这次total正常了
然后回到刚刚这个MYSQL这里,这其实是一些方言,这是一种设计模式,我们可以通过不同的方言来改变这个设计模式的行为。设计模式就是帮你写出更通用,更好维护的代码
方言就是指,比如这个过滤器,它是用来处理SQL的。但是它SQL有很多种SQL,有MySQL,有SQL Server,有Oracle,这些都是属于SQL的方言,他们虽然说SQL的语法都比较像,但他们都有一些特殊的地方,和我们现实中的方言是一样的
统一返回类型
现在我们的增删改查已经写完了
接下来我们会发现一个问题:
就是我们会发现controller里这样返回好像有点不优雅
在企业中貌似也不会像这样直接返回一个int类型,一个bool类型这样,我们一般可以统一返回类型,封装一个响应类BaseResponse,这里再加一个泛型< T >
这个T表示就是Java的泛型,这个T可以代表任何类型。
在实际情况下我们要把它替换为controller返回的int类型,bool类型等等 这是一个通用的返回基类
这里我们也给它加一个Lombok和一个构造函数,但是实际上是不建议的,使用快捷键一样很简单
我们再生成一个ResultUtils,利用这个去给它生成一个成功或者失败的响应类
打上注释:响应工具类
写一个静态方法
这里的code用0比较好,200一般是第三方调用传入的时候用的
再来写一个返回失败
数据data就不需要传入了,传入一个自己定义的错误码和错误信息就好了
整好后我们可以去controller里面改一改
Integer:泛型是不支持基本类型的,所以我们要把它改为封装类
code:40000相当于从客户端传来的请求是有问题的
增删改查都是一样的,复制粘贴就行
在我们使用了这个ResultUtils工具类之后,我们就不用重复地去填充code是0,返回消息是成功等等这些信息了,直接用ResultUtils.succes这一行代码就搞定了
现在我们再运行一下
会发现多了一个code,这个是响应码;data,我们真实响应的数据;message ok 这是我们传递给前端的信息
引入接口插件
因为我们现在没有办法去测试增删改接口,只能去测试查询接口。
(post请求没有办法在浏览器的地址里面直接测)
所以我们接下来引入一个swagger接口插件,去生成一个接口文档。
这里我们不去直接引入swaagger,一个是swagger学springboot的都会接触到,还有一个原因是swagger的默认页面没这个好看,所以我们去引入一个项目,叫knife4j
接口文档有很多,swagger是一个老牌的接口文档,还是比较稳定的。而这个knife4j就是在swagger的基础上给你封装了一层界面,也挺好用的
我们可以来看看它的文档
直接跟着快速开始来
导包,粘贴配置
以下反正都是写不写都行
这里要指定它要扫描的接口它在那个目录下,我们要指定包名到controller
现在差不多就可以用了,我们可以直接执行看一下
http://localhost:8080/api/doc.html
Knife4j 接口文档
这个接口文档已经自动生成了,我们没有在这个controller里增加任何一个注解,它全部交给swagger帮助我们生成了
如果要补充一些什么的话,我们就用@API这些注解去给他打上什么注解就可以了,这点都是一样的
我们测试一下
这也证明了我们通过这个save方法向数据库插入数据之后,这个mybatis就会自动地给我们将数据库中这个新创建的emoji的主键值注入到这个id对象里面,我们只要通过emoji获取id,就能拿到最先插入的这个id值
这个controller可以说差不多搞完了
我们前后端对接一般用的http+JSON格式,然后如果说你要前端给后端发送JSON格式的话,你如果使用springmvc框架,你这里要打上一个注解,叫@RequestBody注解,要不然它是没办法识别出来你前端请求的JSON数据的,必须要加上这么个注解
然后你在controller类的这么一个开头加上@RestController注解的话,它就能给你这个类里面编写的所有的接口的返回值,都去返回JSON格式
这里我们给所有的值都加上@RequestBody注解,getMapping我记得好像是不用加的
JSON格式它会自动映射到这个对象,然后给这个对象赋值,这样就可以接收这个参数了。
接下来我们去做文件上传功能
文件上传
关于文件上传,我们可以先去看一篇文章,分享了一些文件上传的思路,我们可以先去看看
https://blog.csdn.net/weixin_41701290/article/details/119673689?spm=1001.2014.3001.5501
最好理解的就是里面的这个秒传
就是有一个现象,当你第一次上传一个很大的文件时,它也许上传的贼慢,但是如果当你第二次上传这个文件的时候,它会1秒钟刷的一下就给你上传上去了。这个现象就是秒传
原因是因为这个文件,只要我们传过一次,它就传到了别人的服务器上了,我们再去传相同的文件时呢,它会先根据这个文件,去计算一个唯一的值,只要这个唯一的值,它和我们之前存储过的文件它的值匹配上了,我们就不用重复上传了,它会告诉我们上传成功了,然后把我们的这个文件,它的目录,它的文件名,去指向这个,去建立一个映射,去指向那个我们之前已经上传过的那个文件的本体,就好了
总之就是一句话,自己写文件存储文件上传很难顶,而用别人的就是相对来讲比较高可用,会比我们用一个单台服务器来的要稳定安全一些
所以我们用现成的,这里现成的对象存储我们也有很多选择,有腾讯的,有百度的,有阿里的,有七牛云啥的
现在我们是需要一个现成的文件上传服务,就是它给我们一个工具包,我们写几行代码就能直接使用人家的文件上传服务了,至于这个文件它存在那里,怎么管理都不用我们自己操心了
这里我们用腾讯云
我们先去看看最新活动,它这个一般都是有免费体验的
我们来找一下对象存储,cos就是对象存储服务,就是存储文件,存储音视频什么的
这个0元试用,先来白嫖它6个月
领取完这个对象存储资源包之后我们再去整一个流量资源包
领完我们再去领个CDN,就在免费领取COS的旁边
领取完之后,有了对象存储,有了CDN,我们这套服务算是开通完了
我们现在开始正式搞这个文件上传啊
-
首先是接入对象存储的SDK,写一个demo,实现文件上传
-
然后是写一个接口,支持接受文件,上传到对象存储服务中
首先是接入对象存储的SDK,写一个demo,实现文件上传
我们看看这个对象存储怎么用,我们点击这个开始学习,去看看它的这个文档
越好的SDK就是写最少的代码,有同样的功能,给你一些现成的代码去实现功能
导入依赖
引入依赖之后,我们继续往下看初始化客户端
这是一个类似门面模式的东西,就是说我们提供一个统一的类或者对象,然后用来集中提供一些API,用来提供给其他开发者调用。这用就可以把一些实现的细节屏蔽掉了,我们开发者不需要关注他们是怎么实现的这些细节,只需要知道我要给它什么值,它会给我返回什么值,基本上知道这些就够了
我们现在来写一个demo试一下,最简单的就是搞一个main函数
我们来整一个单元测试
对象存储属于一个第三方的服务,我们写一个manager包,然后新建一个TencentCOSManager
写一个test,一个beforeTestAll
我们可以来测试一下这个beforeall注解
就是说:这个beforeAll我们在执行其他测试之前,先执行这个注解下的方法,我们可以在这里面先执行初始化等的操作
如果我们是说想要在执行每个测试方法之前都去执行一遍这个方法的话,我们可以打一个beforeEach注解,再把static去掉
这样就是执行了两边before class了
现在我们改回beforeAll,我们现在可以在beforeAll里面初始化这个客户端
我们去把文档里面的这段代码给它粘过来
这段都是别人给我们的SDK,没必要我们自己写
引入的包是qcloud的包
整理一下
首先是用我们的id和密码去初始化一个COS认证对象
然后第二步是设置这个存储桶的地域,就是说你这个COS存储的地址实在上海还是在北京还是在哪些地方
然后我们去创建一下客户端的配置,
再利用这个客户端的配置去生成一个COS客户端访问对象
好,现在我们现在就在腾讯云的对象存储上去新建一个桶吧
对象存储它一般都是把文件先去分桶去进行存储和管理的
我们先去控制台,进入到对象存储服务,进入存储桶列表
创建存储桶
那么现在问题来了,这个地域我们要填到哪里呢?
应该是离得近的吧
那是离自己近呢,还是离谁近呢
这个主要是要考虑,如果这个你是要给用户用的,那就把这个桶放在离用户近的地方,
再另外,一线城市一般来说也是要比二线城市有限考虑的
所以我们就填广州吧
名称就叫emoji
访问权限的话,共有读私有写会更适合一点
就是不能谁都能往这个里面写入文件,但是呢,大家都能去访问到
默认警告就是如果一份钟之内流量轰的一下就滋上来了,它可以警告我,不然一下子就把我搞穷了
内容安全就是如果有人往这个表情包网站中上传黄赌毒内容的话,它能够给你进行一个自动识别,如果有问题就会给你报错,不让你上传
版本控制:就比如说你文件昨天上传的,然后今天又更新了,它会给你同时保存昨天和今天两个版本的文件
容灾:这个举个例子就是,你的文件是存在广州的嘛,现在广州的电线我给你挖断了,你如果开了这个多个数据中心的容灾的话,那说不定上海也会有你的源文件,这样你的文件就不容易丢失
现在存储桶就创建好了,我们可以上传文件上去了
接下来呢,我们不是要手动地上传文件,而是要自动地上传文件
创建完后,我们要去demo中接入它
我们想要通过SDK将文件上传到腾讯云的服务上,肯定要通过用户校验
可以新建一个Constant存储身份信息
去网址查找id和key
填入id和key
现在我们设置好了,得到了一个身份验证的对象,接下来呢就是要设置我们这个存储桶的地域
去文档中查一下地域的简称
我们拿到后把它粘贴到我们的代码上
然后再往下,就是初始化一个客户端的配置,这里默认的协议我们就不用管了
OK了
我们来试一下这个客户端能不能用
我们去文档里面找一个来试一下
试一下这个查询功能能不能使用
复制粘贴试一下 打个断点debug跑一下
悬停在bucket一会点击加号
就可以出来了
两个桶,刚刚好
说明它已经帮我们把这个桶查询出来了
我们还可以查出名字地域什么的
说明前面的这个客户端我们已经接入成功了
接下来就可以开始新建文件,上传文件就可以了
我们到快速开始中找到上传对象
直接基础款
把这个代码直接粘过来就可以了
这里我们指定一下要上传到的存储桶的名字
还有上传的文件地址:
现在我们要指定一下文件上传的路径,也就是文件名再加上一个统一的前缀
我们可以指定一个Path,以后我们的项目文件都统一上传到他的这个目录下
比如说叫emoji,先用项目名,再加上一个文件类型
path = emoji/img
然后我们上传文件是吧,我们就可以用我们上传的这个文件名称作为路径
我们现在可以在这个里面随便放一个文件
改个名字:
test-img
现在我们去把它的这个路径粘贴过来
然后这个key,就是最终要上传到这个存储桶的路径,就是
写成
我们在输出一下
好,现在我们来运行一下
再去腾讯云里面刷新看一眼
可以看到 我们这里这个目录出来了!我们可以点击预览一下,这个图片就出来了
那么我们这个demo就完成了
接下来我们来做下一步
写一个接口,支持前端上传文件到对象存储服务中
首先,我们是不是要在启动项目的时候,提供一个唯一的客户端对象啊
那我们怎么去提供这样一个唯一的客户端对象呢?
我们可以去写一个bean或者一个config
那我们选择去初始化一个bean,这里可以用到单例模式
但是我们这里不自己去写单例,我们可以利用spring的依赖注入,它可以帮我们去生成一个唯一的对象,不用自己去写单例了
打个注解让他可以注入进去
提供一个bean,让他返回一个COSClient
再把test里面的代码粘贴进去
把这个constant复制上去
好,我们现在在这里得到这个客户端对象,我们直接返回就好了
之后呢我们就可以用spring依赖注入的方式去使用客户端了
接下来我们去controller里面去试一下啊
之前我们是实现了表情的增删改查
现在我们去新增一个接口叫上传文件,返回值还是BaseResponse,整一个布尔值
把之前的代码复制过来
然后呢,我们在这里注入一下
然后这里传入一个什么对象呢
百度搜索一下:springboot 文件上传 controller
这个file就是前端接收过来的文件对象
就是前端传来的文件,我们那这样的一个参数去接收
我们去看看这个博客是怎么处理这个文件的
好像,他这里好像是先把文件上传到了本地
它是先从前端传来的这个文件中,拿到了这个实际的文件名,
然后自己定义了一个文件存储目录,去创建了一个文件 也就是说,他是先接收到前端传过来的文件,再把它放到后台的这样一个临时目录,然后再得到一个file对象,最后再把它上传到对象存储中
复制过来,输出一下
我们来测试一下它给我们传入的这个文件名字是什么样子的
debug一下,我们去接口文档去给他传一个文件试试
可以看出它就是一个文件名,是没有根路径的
好,接下来我们就来把这个文件真实地上传到文件存储中
我们先把这个文件路径名拼一下
这就是我们要上传到后台的路径
那它是怎么把这个文件接收到临时目录再得到这个file的呢
再看回博客的下一行代码
它是先new一个空的文件dest,然后用transferTo方法把前端接收的这个文件上传上去
所以我们先来定义一个本地的路径用来临时存储
然后我们再使用transferTo这个方法,把前端传来的有数据的这个文件,传到我们的dest文件中
第一行可以删掉了 把dest传下去,通过这个COS客户端把有数据的文件上传到远程就好了
别忘了最后返回一个success
好,我们试着来运行一下,去接口文档传一个文件看一眼
报错了
它说系统找不到指定的文件
那我们怎么整呢
我们先在这里,临时新建一个temp 我们就不让它帮我创建目录了,我们自己在本地创建一个目录
复制一下地址,给这个localpath改一下路径
我们再运行试一下
上传上去了
最后做一点收尾工作就结束了
我们在文件上传之后,应该去拿到文件存储的地址
我们简单点,直接域名加key啊
我们去腾讯云配一个CDN
这里有一个域名,我们复制一下
下面还有一个默认CDN加速,我们也配置一下
把这个默认CDN加速给它开启
然后我们保存一下
现在就有了一个CDN加速域名
我们保存一下域名,看一下能不能访问到我们之前的文件
CDN的文件就是我们都有一个源站,CDN是多个不同的节点,离用户近的一些节点。从这个源站中去领取,去读取一些文件,把这些文件提前搬到CDN上,用户只要访问CDN,就可以得到更快的访问
我们去试着看能不能访问到这个图片
用域名加图片路径
那我们Controller这里就直接返回给前端一个上传文件的地址就可以了,这个我们来自己拼
emoji-1308058119.file.myqcloud.com+key
OK,圆满结束!