管理富文本编辑器里上传的文件

问题描述

富文本编辑器会提供插入图片或视频的功能,该功能一般情况时执行插入操作时上传相关文件,由服务器返回静态文件的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'