在开发 Lsky Pro 时,我的目的性很强,开发语言选择了 PHP,就是为了快速迭代出项目,无论是库以及生态,PHP 在这方面有很大的优势,效果也很明显,我仅仅用了 18 天的时间发布了 Lsky Pro 第一个版本。

但使用 PHP 最大的痛点是在于 PHP 的工作模式,传统的 FPM 满足不了 Lsky Pro 的部分场景,例如部分第三方云储存尚未提供删除多个资源的接口(点名又拍云),这就导致了用户进行批量删除时,需要并发请求删除资源接口,最简单的实现方式就是使用队列来进行异步请求,在程序进程中一个一个的删除。

但是实现一个队列,对于程序的安装以及维护成本上是昂贵的,并且还需要考虑到失败重试、消费进程数、同时处理消息数等一系列延伸出来的问题。

没有解决办法吗?有的,上 Swoole,直接异步编程,需要并发处理的操作全部协程化,在协程中处理长时间的任务,并且性能上更是质的飞跃。但是在开发 Lsky Pro 时,我并没有考虑使用 Swoole,原因是对于部分站长很不友好,如果使用的是非集成环境安装的 PHP,通常需要自己编译安装 Swoole,单在这点上就已经劝退了大部分人。

新项目兰空云盘(Lsky Cloud),在进行了一些简单的规划后,开发 Lsky Cloud 我选择了 Golang&Gin&Gorm + Vue&Vuex,Golang 通过关键字 go 就能创建一个线程,且编译速度飞快,对于云盘这种业务逻辑少的程序,Golang 成为了不二之选。

当前预览图:

Lsky Cloud 延续了 Lsky Pro 的 UI,前端使用 Vuetify 框架实现。

目录结构

├─bootstrap 程序初始化文件目录
├─http 程序模块目录
│  ├─controllers 控制器目录
│  ├─middleware 中间件目录
│  ├─validate 验证器目录
├─models 模型目录
├─routes 路由目录
├─runtime 缓存目录
├─storage 物理文件储存目录
├─tools 工具函数目录
├─utils 程序辅助函数目录
├─web 前端脚手架工程文件目录

Lsky Cloud 支持以本地、七牛云、又拍云、腾讯云、阿里云、ftp 的方式储存文件,并且支持客户端分片直传。

上传策略

考虑到以后有更多的第三方储存,前后端在实现上传时,以工厂模式的设计方式,对上传策略部分进行封装,以便以后拓展更多的上传策略。

image-20210208143823019

每个 policy 中所有的方法、事件名相同,增加一个 policy 是只需要实现其中的方法即可。

上传流程

Lsky Cloud 上传文件时,会进入一个队列,队列我使用 tiny-async-pool 实现,为了避免分片请求过多导致请求 padding,每次只会有一个文件出队上传,单个文件会进行分块处理,每个块异步上传。

入队的文件首先获取所选文件的 hash md5 值,也就是在客户端计算 md5,我选择了 CryptoJS ,为了改进性能,计算时会进行文件分片,逐个计算,完成速度取决于客户端性能。

在计算 md5 前会向服务器请求获取文件的基本大小,判断文件格式是否允许上传、用户储存空间是否足够等,同时获取该文件应该存在的目录路径。md5 计算完毕后开始正式向第三方储存 API 上传文件,出现错误时则会跳过,处理队列中的其他文件。

由于部分第三方储存不支持异步回调功能,所以回调验证部分只能需要自己实现,设计成了文件上传成功后由客户端发起回调请求,告诉服务器该文件已被上传完毕。

起初想以携带文件元数据(meta)的方式储存文件的一些基础数据,比如文件类型、md5、sha1 值等,这样的话就可以在上传完毕后很方便的创建该文件记录,无奈在我的百般尝试下,又拍云的 REST API 居然在设置 meta 时会出现跨域的问题,询问客服才知道 REST API 无法设置自定义的 meta,只能作罢。

回调通知到服务器后,将会重复执行上传前的验证操作,避免非法伪造的回调。

文件夹

Lsky Cloud 中可以新建“文件夹”进行文件分类,为了更方便的实现文件夹加密功能,该文件夹并非物理文件夹,而是类似一个分类功能。

image-20210208144037061

文件管理

Lsky Cloud 在磁盘文件管理上下了很大的功夫,最终实现了与 Windows 资源管理器类似的交互体验,得益于 Vuetify 强大的 Menus 组件,很轻松的实现了右键菜单的操作,以及各种 Material Design 风格的响应式对话框。

其次就是拖拽、多选文件夹等操作,这部分实现比较繁琐,首先需要监听鼠标点击、松开、以及拖拽事件,在鼠标移动时根据元素以及父元素宽高来判断选框是否覆盖,并遍历被覆盖的 object,被覆盖的 object 同时需要改变其被选中样式,开始是使用 vue 的双向绑定 class 实现动态改变样式,但实际测试过程中,被绑定的数据是存在于 vuex 中的,鼠标在拖拽过程中会频繁执行 vuex 的 commit,导致性能低下,元素过多的情况下会出现卡顿现象,尝试直接使用 Jquery 修改元素,问题解决。

然后就是文件拖拽到指定文件夹的操作,我使用 vuedraggable 实现,这个组件主要是拖拽替换位置的,不过它可以重写 move 方法,自定义拖动、被拖动的方法,实现自己的需求。

代码节选:

image-20210208142840585

可以看到,部分事件中使用了 Jquery 进行动态修改、删除元素的 class,以及元素的显示与隐藏。当然了,在 Vue 中,新手需要注意双向绑定的数据中 Jquery 事件绑定优先级等问题。

本地上传

本地和 FTP 上传策略,都是直接走的服务器流量,由于安全性问题,FTP 策略需要从服务器中转后上传,前端使用百度的 Web Uploader 实现,该组件支持分片上传、队列等功能。

文件上传到后端以后,验证完成后会在本地磁盘创建唯一的分片目录,每一个分块通过分块序号进行命名,全部分块都上传完毕后进行合并,然后重复验证文件大小、以及是否允许上传等。

心路历程

Lsky Cloud 在几个月前就开始筹备,在简单的学习了 Golang 后便着手利用空余时间开发,虽然是断断续续的提交代码,但是期间积累了不少经验。

中间遇到了主线逻辑中比较难解决的问题,差点就放弃了,不过在几天后突然就冒出一个想法,顺着这个想法便解决了问题。

目前还有很多需要完善的地方,目前重心都在雕琢用户体验的部分,耗费的大部分时间也都在前端上,希望可以做出一个令自己满意的产品。

说点什么吧~ 取消回复
共有 9 条评论
  • okok Windows 10 x64Google Chrome

    昨天 21:16

    回复
    非常好的图床,请问支持虚拟主机安装吗。要是在增加github上传就厉害了,
  • wodesd Windows 10 x64Google Chrome

    2月22日 16:28

    回复
    支持作者,期待中
  • Seamonster Windows 10 x64Google Chrome

    2月22日 15:01

    回复
    哇哦,开始玩Go啦~不错哦,我觉得基于 vue 的 elementUI 更加现代化,可以试试
  • 刘老板 Windows 7 x64Google Chrome

    2月20日 16:35

    回复
    加油!支持!很期待。
  • Louis_K Windows 10 x64Firefox

    2月10日 12:30

    回复
    作者你好,我是一个建筑与室内设计师,目前处于贫困的状态,我缺乏技术与金钱,我认为建筑设计与程序设计一样,完全奔着金钱而去做一件事不会有好的作品,在做自己的建筑论坛过程中,因缘之间,我接触到了您发布的这款完全开源免费的产品,而且还在PHP文件里标明了各种语句意义。产品又那么迅速好用,也看了您的日志,非常感动。您在我心目中是高大上的的形象,意识高尚。在这里,我非常感谢您的产品,他日经济好转我必然会向您支付大笔捐助,我想这是我表达感谢的最好的方式
  • swordpal macOS 10.15.6Safari

    2月8日 23:18

    回复
    大佬的作品棒,文章思路也是超清晰,读下来舒坦!🤩
Wisp X

不妄自菲薄,不矫枉过正; 不随波逐流,不固步自封。

欢迎访问熊二哈的猫窝,本站建议使用 Chrome 浏览器浏览以获得最佳效果。