0%

问题描述

富文本编辑器会提供插入图片或视频的功能,该功能一般情况时执行插入操作时上传相关文件,由服务器返回静态文件的url地址,前端通过插入img或是video标签,并将返回的url赋予src属性,这样就可以在前端看到我们插入的文件了,但是用户可能会插入新的文件或是删除已插入的文件,但是在前端我们只是删除img或是video标签,服务器上该资源的文件还是存在的,并没有被清除,事实上此时用户没有提交,我们也不能直接进行删除,毕竟用户还可能执行撤销操作,如果直接删除了服务器资源,就会导致文件资源404,找不到该资源,又要重新上传,用户体验太差。

解决思路

前端提供需要清除文件的地址数组

用户打开富文本编辑器,这可能是新建的或是对已有的内容进行编辑,我们可以初始化一个数组let uploads = [],如果是编辑操作,我们先通过querySelectorAll('[src]')得到文件相应的 dom 数组,后面进行遍历,将每一项的src值push到uploads数组里,这样我们就得到了以前上传并使用中的文件列表;在后续的上传操作中,每次上传我们都会将文件地址push到uploads数组里;当用户进行提交操作时,我们可以遍历出内容中所有的file并push到 let files = []数组中。

此时我们得到了两个数组,一个是 uploads 上传文件的数组,另一个是 files 内容中有效的文件数组,那取其差集,就是我们要清除的无用文件的数组 useless了,将该数组传到服务器,就可以清除相关的无用文件了。

这里有个小问题,如何取两个数组差集的问题,可以这样写:

let uploads = ['a', 'b', 'c', 'd', 'e', 'f']
let files = ['b', 'e']
let useless = []
for (let i=0; i<files.length; i++) {
let index = uploads.indexOf(files[i])
uploads.splice(index,1)
}
useless = uploads
console.log(`useless: ${useless}`)

这种实现由什么缺陷呢:整体上看,这种实现确实了我们的预期功能,并且后台也很简单,只需提供一个可以接收删除文件数组的接口,并执行删除就可以了;现实其实后台并没有这么简单,删除操作一般是危险的,需要我们警惕的,如果该实现上传到服务器的文件并没有进行数据库记录,只是放置在 static/uploads/ 的目录下,那就没有办法设置谁由权限删除某一个文件,也就是说只要提供删除数组,所有的可以操作编辑器的用户都可以进行删除操作,显然这是有安全问题的,所以对上传的文件一定要做数据库记录,并设置所有者,执行删除操作时也需要判断操作是否来自所有者。

既然上述存在的问题必须通过添加数据库记录来解决,那我们可以将上传的文件绑定到指定的内容id;对于新创建的内容,新上传的文件记录的内容id为null,当我们提交编辑器内容时,我们将files各记录的内容id设置为实际值;如果是编辑已有的内容,我们此时已有该内容的id,将该内容id下的所有文件记录的内容id设置为null,我们将files里的指定为该内容id,之后我们可以定期清理内容id为null的文件,即可清除无用的上传文件,这有点类似于js中内存垃圾清理实现方式之一的引用计数。

id article_id file_url
0 null http://domain.com/pic.jpg'
1 1 http://domain.com/pic.jpg'
2 1 http://domain.com/pic.jpg'
3 1 http://domain.com/pic.jpg'
4 1 http://domain.com/pic.jpg'
5 1 http://domain.com/pic.jpg'

安装

sudo apt-get install mongodb

错误处理

用以上命令安装完成后,执行 mongo 命令,并没有连接成功

MongoDB shell version: 2.4.14
connecting to: test
Wed Jul 17 22:40:55.765 Error: couldn't connect to server 127.0.0.1:27017 at src/mongo/shell/mongo.js:145
exception: connect failed

查看安装打印的信息中,有这么一条 无法创建主目录"/var/lib/mongodb", 说明 mongodb 缺少权限

sudo service mongodb stop
sudo chown -R mongodb:mongodb /var/lib/mongodb
sudo service mongodb start

修改目录权限后再次执行 mongo 命令,显示连接成功

MongoDB shell version: 2.4.14
connecting to: test
Welcome to the MongoDB shell.
For interactive help, type "help".
For more comprehensive documentation, see
http://docs.mongodb.org/
Questions? Try the support group
http://groups.google.com/group/mongodb-user
Server has startup warnings:
Wed Jul 17 22:50:05.612 [initandlisten]
Wed Jul 17 22:50:05.612 [initandlisten] ** NOTE: This is a 32 bit MongoDB binary.
Wed Jul 17 22:50:05.612 [initandlisten] ** 32 bit builds are limited to less than 2GB of data (or less with --journal).
Wed Jul 17 22:50:05.612 [initandlisten] ** See http://dochub.mongodb.org/core/32bit
Wed Jul 17 22:50:05.612 [initandlisten]
>

安装MongoDB服务

这里下载的时MongoDB 4 Windows社区版本,4之后的版本跟之前的有很大的不同,不需要自行指定数据库和日志目录了,这里安装过程只修改了安装目录,从默认的c盘自定义为d盘,其他均为默认选项,一并安装了compass,这里大多人都不推荐安装compass,说是会卡住,这里安装确实看起来像是卡住了,但就如安装信息说的这里可能需要 a few minutes,事实上这里安装进度不变保持了10分钟左右就安装好了,所以不用太担心,只是网络原因,假死而已

当然为了方便连接 MongoDB 实例,记得要将MongoDB安装目录下的bin目录添加到系统变量,这样我们就可以直接在shell 里直接输入 mongo 即可连接到MongoDB服务

常用命令

mongo

连接到本地的 MongoDB 实例,使用默认服务端口 27017

如果修改了本地 MongoDB 实例默认的服务端口,连接时我们也可以通过 --port 参数指定端口

例如

mongo --port 28015

show dbs

查看所有的数据库

db

查看当前数据库

use DATABASE_NAME

切换数据库(不论存在与否)

db.DATABASE_NAME.insert()

向 DATABASE_NAME 数据库插入数据

例如

db.test.insert({"description": "这是一条测试数据"})

注: 该命令不需要切换数据库

db.dropDatabase()

删除当前数据库

db.createCollection(name[,options])

在当前数据库创建集合 COLLECTION_NAME

db.COLLECTION_NAME.drop()

在当前数据库删除集合 COLLECTION_NAME