今天的心得比较多,可能写的会比较多!主要完成了相册和图片的优化,前台展示以及博客页上传图片功能的最终实装!
图片存储方面的优化
redis状态验证
在之前的图片上传模块中,发现图片到上传图床的时间跨度非常大。而且,一旦出现错误,各个文件和记录的回退(删除)会显得非常浪费资源。
于是乎,我选择了先查询redis中保存的图床状态,如果确保图床可连接才开始所有流程,从而提前抉择是否执行后续较为复杂的业务。
当然了,图床状态每次进入管理员后台的时候都会自动在概览页调用查询一次,而要上传的前提肯定会进入到后台页面,所以达成完美的契合。
状态检测当然很简单的一个连接尝试语句,默认认为一秒没有回应就算状态异常了:
代码语言:javascript复制InetAddress.getByName(host).isReachable(1000)?1:0
将结果计入redis中,方便快速调用,后续请求上传服务的时候,会先取出该值,如果为1才继续执行,否则返回错误码。
更改图片记录设计
之前图片记录计入数据库时,网址是目标图床的完整地址,这将有很多隐患,比如说后续如果图床服务出现故障,那么改串会比较麻烦,而且这些链接的前缀都是一致的,存储大量同样的片段,是一种资源浪费,故,最终决定图片位置只存储相对位置,即其uri,例如原本可能是:
http://localhost:9999/img/1/abc.png, 现在只存储img/1/abc.png。
那么是否将前缀交给前端拼串呢?我个人认为放在后端拼串效率会比放在前端来的高,因为前端本身浏览器本身的处理能力有限,放在后端应该能让用户有更好体验。
以后如果图床出现问题后,可以快速方便迁移。
图片上传功能的实现与问题
前端上传图片
一开始,打算只用单纯的form表单节点进行图片上传,结果发现这种想法完全不可行。因为图片上传需要有鉴权,单纯的上传并不能鉴权(大概?)。所以最后还是老老实实的用ajax进行数据请求了。
默认的表单提交会刷新页面,完美当然不想要,所以需要组织表单默认提交事件,图片上传要求必须有enctype且值为multipart/form-data,在发送ajax请求时,也需要设定好content-type:multipart/form-data 。需要指定上传文件的格式,input file有属性 accept="image/*" 表示接受所有图片格式的文件。所以最后html代码如下:
代码语言:javascript复制<form action="/api/image/add" method="post" enctype="multipart/form-data" @submit.prevent="beforeSubmit()">
<input name="name" placeholder="更换名字" v-model="tempName"/>
<input name="file" ref="file" type="file" style="width:200px;border:1px dashed black;margin:10px" accept="image/*"/>
<input name="describe" placeholder="添加描述" v-model="describe" style="margin-right:10px"/>
<input type="submit"/>
</form>
然后相应的scss代码如下(鉴权后面在说,这里先当没这回事):
代码语言:javascript复制const req = new FormData()
req.append('aid', this.aid)
req.append('name', this.tempName)
req.append('time', this.$time())
req.append('describe', this.describe)
const imgFile = this.$refs.file.files[0]
req.append('file', imgFile)
axios.post('/api/image/add', req, {
headers: {
'Content-Type': 'multipart/form-data'
}
}).then(res => {
if (res.data.code === 200 && res.data.data === 1) {
this.freshImg()
alert('添加成功!')
} else {
alert('添加失败!!!!!!!ndata:' JSON.stringify(res.data))
}
}).catch(err => err)
也许你注意到了this.$time(),那当然是我简单封装的按yyyy-MM-dd HH-mm-ss格式返回当前时间的函数啦,只不过装到了vue的prototye原型上。FormData类是js原装的哦,所以不用自己封装了。
至于博客页上传图片就很简单了,按照富文本编辑器抛出的勾子,正常发送请求就好了。
后端上传的问题
在彻底实现上传功能的时候也遇到和解决了不少大大小小的问题。
其一,在自己设置的时间格式工具类中,把小时的H写成了小的h,导致计时方式不同,一个是24h制,一个12h制。
代码语言:javascript复制Calendar calendar = Calendar.getInstance();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return sdf.format(calendar.getTime());
其二,在向图床服务器发送图片时,类型默认为application/x-www-form-urlencoded,当然虽然图片确实保存成功了,不过,点击链接的时候,并且自动由浏览器跑到解析页预览,而是直接启动了下载流程。并不符合我的需求。所以,需要设置请求参数的内容类型为image/png之类的图片格式,据说image/*也是不行的,还没有尝试,大概的确如此吧。
其三,这是个比较严重的问题了,那就是业务逻辑混乱,现在也没有改的非常的好。先说说原由:有两种不同的上传请求方式,一种是表单上传,一种则是博客页富文本编辑器内的隐式上传,隐式上传只需要一个既有的aid即可上传,而表单上传,封装了描述图片的各项信息,一开始没有理清逻辑,导致两个控制器和对应的两个服务形成了一个 Z 字形状,好家伙,调用表单上传方式请求的时候,直接把图片上传记录插入了两条,一条是隐式服务的服务提供的,一个是自己服务提供的,记录部分属性还不一样!现在其实也没想到一个很好的措施,目前只是把多余的一条服务给@Deprecated了。暂且跑起来没问题,仔细想想也许两个请求可以合并合并?不过那样对返回值格式必须靠向别人的富文本编辑器需求的回应格式,不爽,所以不删!(●ˇ∀ˇ●)
其四,是第一次真实业务修改表字段的定义,记好啦!ALTER TABLE <table> CHANGE COLUMN <col> <col> <type> [con] [con]
代码语言:javascript复制ALTER TABLE album CHANGE COLUMN cover
over INTEGER NOT NULL DEFAULT 1;
细说前端
js从文件读取内容方式
原本富文本导入导出(防止一些特殊情况发生)用的是window.prompt()方式,好家伙,导出来直接去除了所有回车换行,所以还是输出到控制台以及从文件导入了。
代码语言:javascript复制const _that = this
const bak = this.$refs.bak.files[0]
const bakReader = new FileReader()
bakReader.readAsText(bak)
bakReader.onload = function () {
_that.editor.txt.html(this.result)
}
vue获取事件对象
之前没有对事件对象操作太多,这次正好用到了,需求是,鼠标滚轮滚动,然后滚动条水平滚动,此时调用的时候不加(),第一个参数即自动赋值了事件对象。
浅拷贝与深拷贝
js也同样有着浅拷贝和深拷贝,例如let a = let b = [ 1, 2, 3, 4 ], 此时a和b指向的是同一处地址。
盒子水平滚动
属性 white-space: nowrap; 表示默认不换行,配合 overflow-x = auto 即可让内部若干盒子滚动。只是实现鼠标滚轮滚动的动画还是比较难的,目前的实现比较草,以后完善吧。注释的是尝试失败的滚动动画算法。还亏我使用了mutex锁。
代码语言:javascript复制scrollList (e) {
// this.mutex = false
const delta = e.deltaY
const node = this.$refs.imgList
if (this.tX delta > node.scrollWidth) {
this.tX = node.scrollWidth
return
}
if (this.tX delta < -node.scrollWidth) {
this.tX = 0
return
}
// const judge = delta > 0
// setInterval(() > {
// if (judge && this.fX < this.tX) {
// node.scrollTo( this.fX, 0)
// this.fX = 2
// } else if (!judge && this.fX > this.tX) {
// node.scrollTo(--this.fX, 0)
// this.fX -= 2
// } else {
// return
// }
// console.log(1)
// }, 10)
// if(delta>=0) {
// while (this.fX < this.tX) {
// node.scrollTo( this.fX, 0)
// }
// }
node.scrollTo(this.tX, 0)
this.tX = delta
// this.mutex = true
}
待优化的部分
现在,每次新建专辑或者上传照片,目前的逻辑是重新申请获取一下列表,仔细思考感觉这样对性能上来说,并非是好的,目前还在思考最佳的解决方案。后续的某天来进行优化。
快到两点了,啊啊啊啊,鉴权的部分拖了几天没写了,今天晚上一定写!今天是周五了,这周还有三天,争取把前端这些核心需求给完成了。后面可以好好休息一活儿。就这样子啦,
每天见!
对了,既然实现了,那么尝试上传一张图片吧!下图就是上传的哦,成功了吗?