开源一个离线版Alist,无需服务端部署即可管理云盘文件
Alist 是一个非常不错的云盘文件管理程序,不过需要一台服务器或者 Nas 进行部署,当然,这也同样便于在网络分享文件。但如果只是个人使用,服务器部署以及多账户系统未免有些麻烦。我是一个怕麻烦的人,虽然我自己也在 Nas 中部署了一个 Alist 服务,但还是想要一个单独的 离线版APP,红枫云盘 就由此诞生。
红枫云盘 即 maple-file,名称由 maple(枫叶) + file(文件) 组成, 这原本是我很早之前写的一个基于 Flask 的项目,主要用于图片上传,还有相册的功能,但已经很久没有使用和维护过了。本来我准备把新项目起名为 Filesea,但又和我的另一个类似 Alist 的项目名相同(未开源),而且 Filesea 和一个知名项目 Seafile 过于相似,所以还是继续沿用之前的名称,毕竟对比之前的纯API也算得上是一个全新的升级和改版
功能
目前已经支持的存储:
-
local(本地文件)
-
smb
-
ftp
-
sftp
-
webdav
-
alist
-
又拍云
包括了文件上传、下载、重命名、移动、复制、创建文件夹、预览等功能,虽然部分功能比如预览还比较简陋,但持续优化中,未来可能会添加相册,文件同步和备份等功能
-
文件列表
-
文件预览: 支持文本文件,图片,音频,视频
-
上传和下载
-
重命名、移动、复制(包括多选)
-
添加修改存储
-
设置
使用
目前提供了Android版本和MacOS版本的下载,下载地址: https://github.com/honmaple/maple-file/releases/tag/v1.0.0
开发
我参考了 Gopeed 和 Pho 两个项目,客户端使用 Flutter,后端服务使用 Go,通过 gomobile 把 Go 服务打包成 aar(安卓),或者利用 buildmode 打包成 .dylib(MacOS), 这样就能把服务端嵌入到本地客户端,来达到 serverless 的目的。
如果想要添加更多存储,只需要使用 Go 创建一个文件系统接口
1type FS interface { 2 WalkDir(context.Context, string, WalkDirFunc) error 3 List(context.Context, string) ([]File, error) 4 Move(context.Context, string, string) error 5 Copy(context.Context, string, string) error 6 Rename(context.Context, string, string) error 7 Remove(context.Context, string) error 8 MakeDir(context.Context, string) error 9 Get(string) (File, error) 10 Open(string) (FileReader, error) 11 Create(string) (FileWriter, error) 12 Close() error 13}
然后注册一个新的存储名称
1// 注册服务 2func init() { 3 driver.Register("alist", func() driver.Option { 4 return &Option{} 5 }) 6} 7 8 9// 加载服务 10import ( 11 _ "github.com/honmaple/maple-file/server/pkg/driver/alist" 12)
最后在客户端创建一个用于提交配置的表单即可,扩展性足够简单,也足够方便
1... 2DriverFormField( 3 label: "用户".tr(context), 4 value: _option["username"], 5 isRequired: true, 6 onTap: (result) { 7 setState(() { 8 _option["username"] = result; 9 }); 10 11 widget.form.option = jsonEncode(_option); 12 }, 13), 14DriverFormField( 15 type: DriverFormFieldType.password, 16 label: "密码".tr(context), 17 value: _option["password"], 18 onTap: (result) { 19 setState(() { 20 _option["password"] = result; 21 }); 22 23 widget.form.option = jsonEncode(_option); 24 }, 25),
