Compare commits

...

460 Commits
0.3 ... 4.1.2

Author SHA1 Message Date
zhaojun
e9a85a4e88 🔖 发布 4.1.2 版本 2022-09-20 18:30:40 +08:00
zhaojun
63edbc11d3 🐛 修复在 Linux 下开启后台登陆图片验证码时,出现异常提示的 BUG 2022-09-20 18:30:22 +08:00
zhaojun
c98c6af0f9 🐛 修复事务中读取到旧缓存的 bug 2022-09-20 18:30:03 +08:00
zhaojun
47f6066733 🔨 重构代码,将捐赠版已验证的功能合并 2022-09-20 18:12:01 +08:00
zhaojun
f4fb471da3 💄 更新前端页面 2022-09-20 18:08:19 +08:00
zhaojun
301b6bdf70 🐛 修复没有 origin 情况下,跨域失效的问题。 2022-09-09 14:36:22 +08:00
zhaojun
a98c4f4c48 📝 文档更新,增加 3d 文件预览图片 2022-08-29 15:32:44 +08:00
zhaojun
f4a625dac6 🔖 发布 4.1.1 版本 2022-08-29 14:56:39 +08:00
zhaojun
1c2f3de55c 更新文档 2022-08-29 14:56:20 +08:00
zhaojun
ed375768f5 Merge remote-tracking branch 'origin/main' 2022-08-29 14:55:07 +08:00
zhaojun
b0c4ed1fad 💄 更新前端页面 2022-08-29 14:54:59 +08:00
zhaojun
8d30ca7eee 🐛 修复通过 nginx 反向代理后, 未登录后台时请求, 重定向到反代前地址的 bug 2022-08-29 14:44:12 +08:00
zhaojun
43aba8e20e 修改系统设置 value 值字段类型为 text, 防止过长 2022-08-29 14:39:08 +08:00
zhaojun
a2fa8b3eeb 🐛 修复没有自动创建数据库目录的 bug 2022-08-29 14:38:47 +08:00
赵俊
0ef77ee11b Update README.md
增加在线预览 Office 文档截图
2022-08-27 09:09:03 +08:00
zhaojun
6608c0b456 Merge remote-tracking branch 'origin/main' 2022-08-26 18:19:04 +08:00
zhaojun
3ceb5c8c1b 🔖 发布 4.1.0 版本 2022-08-26 18:18:44 +08:00
zhaojun
7399c89a8e 批量删除文件和批量删除直链功能 2022-08-26 18:18:19 +08:00
zhaojun
477c9dbdd2 优化代码结构 2022-08-26 18:17:58 +08:00
zhaojun
e8117c7d3b 增加 OnlyOffice 集成 2022-08-26 18:16:53 +08:00
zhaojun
b29ff1e646 增加 Google Drive 支持 2022-08-26 18:16:00 +08:00
zhaojun
774a8e184a 优化 OneDrive/SharePoint 获取令牌相关代码 2022-08-26 18:14:25 +08:00
zhaojun
7da1405798 💄 更新前端页面 2022-08-26 18:12:07 +08:00
赵俊
564770ba3c Update README.md 2022-08-16 18:45:11 +08:00
zhaojun
ac07de4e73 Merge remote-tracking branch 'origin/main' 2022-08-15 17:11:18 +08:00
zhaojun
3933eba99a 🔖 发布 4.0.10版本 2022-08-15 17:10:03 +08:00
zhaojun
27e8f7961e 🔖 发布 4.0.9 版本 2022-08-15 17:08:57 +08:00
zhaojun
55843cbef6 💄 更新前端页面 2022-08-15 16:35:17 +08:00
zhaojun
ebbb33409f 优化 OneDrive/SharePoint 获取 token 体验,增加信息显示,并优化页面效果。 2022-08-15 13:29:35 +08:00
zhaojun
b39360791f 🐛 修复因缓存未清理导致兼容 readme.md 功能修改无效的 bug 2022-08-15 13:28:35 +08:00
zhaojun
9833378e25 🐛 修复 SharePoint 世纪互联自定义 api 失败的 bug 2022-08-15 13:28:10 +08:00
zhaojun
31df2d16e2 🔖 发布 4.0.9 版本 2022-08-11 21:09:06 +08:00
zhaojun
f8f07912a1 🔨 规范代码 2022-08-11 21:06:40 +08:00
zhaojun
edf43954c6 兼容捐赠版数据文件功能 2022-08-11 21:06:16 +08:00
zhaojun
5a816b1dfb 增加 readme.md 兼容模式 2022-08-11 21:05:25 +08:00
zhaojun
910406c33a 增加最大同时上传数限制 2022-08-11 21:05:18 +08:00
zhaojun
543f76ad1d 增加 readme.md 兼容模式 2022-08-11 21:04:45 +08:00
zhaojun
1cc2d874a1 完善 OneDrive SharePoint 自定义 api 体验 2022-08-11 21:03:11 +08:00
zhaojun
18de372bf9 🐛 修复批量删除直链或直链下载日志数量过多时, 无法正常删除的问题。 2022-08-11 21:02:07 +08:00
zhaojun
5748644814 🐛 修复批量删除直链或直链下载日志数量过多时, 无法正常删除的问题。 2022-08-11 21:02:02 +08:00
zhaojun
fb8060d316 💄 更新前端页面 2022-08-11 21:01:41 +08:00
zhaojun
a4005defe2 更新 github issue 模板 2022-08-05 16:02:34 +08:00
zhaojun
4c90d5bdda 🔖 发布 4.0.8 版本 2022-08-05 15:30:13 +08:00
zhaojun
fa46850bb4 💄 更新前端页面 2022-08-05 15:29:08 +08:00
zhaojun
a7551fce53 完善代码结构 2022-08-05 15:28:52 +08:00
zhaojun
0591669ec9 增加获取单个文件接口请求参数 2022-08-05 15:28:33 +08:00
zhaojun
6286a9e9aa ✏️ 优化提示信息 2022-08-05 15:28:24 +08:00
zhaojun
c67ee1e89d 增加获取单个文件接口 2022-08-05 15:27:58 +08:00
zhaojun
c75695c63a 🐛 修复因类调用自身方法导致事务启用失败的 bug 2022-08-05 15:27:38 +08:00
zhaojun
8f771c652d ✏️ 优化 minio 提示信息 2022-08-05 15:25:52 +08:00
zhaojun
d6e13d6115 🐛 修复获取 m3u8 直链后系统报错的 bug. 2022-08-05 15:25:09 +08:00
zhaojun
8460d17b07 🔨 移动无用代码 2022-08-05 15:24:46 +08:00
zhaojun
49806221b4 💡 增加代码注释 2022-08-05 15:23:36 +08:00
zhaojun
409af8409d 🔨 重构代码,修改包名 2022-08-05 15:22:36 +08:00
zhaojun
6647efeb03 📝 完善 OneDrive SharePoint 反代域名参数的描述信息 2022-08-02 10:25:22 +08:00
zhaojun
c8bd52e26a 统一 sftp、ftp、webdav 下载文件 contentType 为 application/octet-stream, 避免浏览器自动进行默认预览动作 2022-08-01 15:20:06 +08:00
zhaojun
27db6ed14a 🐛 修复本地存储下载时,错误的 contentType 类型导致无法正常下载的问题 2022-08-01 15:17:29 +08:00
zhaojun
b079d03753 🔖 发布 4.0.7 版本 2022-07-31 15:07:11 +08:00
zhaojun
edf8e114ad 💄 更新前端页面 2022-07-31 15:06:55 +08:00
zhaojun
67a84edd4d 优化 S3 协议自动配置跨域逻辑,改为不覆盖原有配置。且增加 GET 跨域,对于在线预览文本、视频场景提供跨域支持. 2022-07-30 20:01:41 +08:00
zhaojun
d798750ee6 优化 S3 协议自动配置跨域逻辑,改为不覆盖原有配置,修改描述. 2022-07-30 20:00:18 +08:00
zhaojun
fea1da86fc 🐛 修复 minio 设置跨域上传报错的 bug 2022-07-30 18:45:19 +08:00
zhaojun
96a0c90600 ⬆️ 更新 pom 依赖,去除无用依赖,升级有漏洞的依赖,划分相关的依赖 2022-07-29 16:50:08 +08:00
赵俊
a054e82740 Merge pull request #404 from wswm152/main
解除对过期内容的引用
2022-07-29 16:19:08 +08:00
无始无名
dc0e84e1e3 解除对过期内容的引用
部分内容在jdk8以上版本中已被去除
2022-07-29 15:05:34 +08:00
zhaojun
ae31005959 🔖 发布 4.0.6 版本 2022-07-28 21:27:18 +08:00
zhaojun
0f167a304d 独立直链和短链功能 2022-07-28 21:24:38 +08:00
zhaojun
43d8143c74 💄 更新前端页面 2022-07-28 21:23:42 +08:00
zhaojun
b2fb1af99b 📝 修复本地存储文件路径描述,对于 docker 环境更容易理解。 2022-07-27 20:49:39 +08:00
zhaojun
a4f4d654a3 ✏️ 修改、完善判断隐藏文件夹方法的代码注释 2022-07-16 17:42:14 +08:00
zhaojun
04d9c24b43 📝 修改文档 docker-compose 错误部分 2022-07-15 22:52:13 +08:00
zhaojun
91a7092190 🔖 发布 4.0.5 版本 2022-07-15 20:50:35 +08:00
zhaojun
f82155f157 💄 更新前端页面 2022-07-15 20:45:29 +08:00
zhaojun
a99218dfa5 🐛 修复 4.0.4 版本更新导致的新增存储源错误的 bug, 其他版本不影响 2022-07-15 15:08:23 +08:00
zhaojun
3e9fe27890 🐛 修复本地存储,文件重命名,前后名称一致时,会无限创建子目录的 bug 2022-07-15 15:07:53 +08:00
zhaojun
7524f58928 💄 更新前端页面 2022-07-15 08:47:25 +08:00
zhaojun
6499f2d220 💄 更新前端页面 2022-07-15 08:47:11 +08:00
zhaojun
40b1f1bc2d 🔖 发布 4.0.4 版本 2022-07-14 20:28:58 +08:00
zhaojun
3bd859d705 ✏️ 删除无用代码 2022-07-14 15:03:59 +08:00
zhaojun
6156d85ba3 🐛 修复启动匿名操作失败的 BUG 2022-07-14 15:03:03 +08:00
zhaojun
e1df2f886b 🐛 修复 url 路径中带 . 访问 404 的 bug 2022-07-13 20:18:48 +08:00
zhaojun
cea42a57bb 🔖 发布 4.0.3 版本 2022-07-13 20:14:24 +08:00
zhaojun
c123d2a671 💄 更新前端页面 2022-07-13 20:13:12 +08:00
zhaojun
02e00b765b 💄 更新前端页面 2022-07-13 20:12:05 +08:00
zhaojun
1992c1f52d 📝 增加异常情况的提示信息 2022-07-13 20:11:50 +08:00
zhaojun
4eb9a721fc 🐛 修复本地存储、FTP、SFTP、WebDav 未启用私有空间,仍然校验私有签名的 bug 2022-07-13 20:10:08 +08:00
zhaojun
f1bac40af4 ✏️ 删除无用代码 2022-07-13 11:19:42 +08:00
zhaojun
0a7fb41e81 🐛 修复 readme 文档仅在首页显示的 bug. 2022-07-13 11:08:30 +08:00
zhaojun
4fd0b8d442 🐛 修复 s3 存储类型不开启私有空间时,文件路径或文件名包含中文无法直链下载的 bug 2022-07-13 11:08:05 +08:00
zhaojun
28150b0c1a 🐛 修复直链下载日志表中 ip 字段长度不足矣存储 ipv6 的 问题 2022-07-13 11:06:29 +08:00
zhaojun
a7813ff7bc 🔖 发布 4.0.2 版本 2022-07-12 19:52:56 +08:00
zhaojun
ae500ef9dc ⬇️ 移除无用依赖,减小项目体积 2022-07-12 19:52:40 +08:00
zhaojun
29d5b84df2 📝 更新文档, 更换图片预览的示例图 2022-07-12 19:49:11 +08:00
zhaojun
28882b198c 💄 更新前端页面 2022-07-12 19:48:42 +08:00
zhaojun
d624c886fe war 打包方式 ipv6 支持 2022-07-12 19:36:45 +08:00
zhaojun
e08654a59e Merge remote-tracking branch 'origin/main' 2022-07-12 19:34:53 +08:00
zhaojun
8fa6a5c9b8 💄 更新前端页面 2022-07-12 19:34:20 +08:00
zhaojun
7620a578fb 服务器代理上传支持特殊字符的文件名,如 # 2022-07-12 19:33:49 +08:00
赵俊
9c9bf93f00 Update README.md 2022-07-11 19:17:54 +08:00
赵俊
912eb694cb Update README.md 2022-07-11 19:11:08 +08:00
zhaojun
78d5ba90cf 💄 更新前端页面 2022-07-11 17:33:53 +08:00
zhaojun
056f9d4449 💄 更新前端页面 2022-07-11 17:33:37 +08:00
zhaojun
9a6020dcc8 增加文件点击习惯设置, 支持设置为单击还是双击 2022-07-11 17:29:15 +08:00
zhaojun
6b3025f379 fix(直链): 统一下载日志 storage_key 字段长度, 防止存储源长度过长后无法正常记录下载日志. 2022-07-11 14:57:30 +08:00
zhaojun
9dffaaee25 fix(异常处理): 存储源不存在时进行提示,修复后提示存储源不存在 2022-07-11 14:56:27 +08:00
zhaojun
bda654a012 fix: 修复管理员修改密码无效的 bug 2022-07-11 14:54:26 +08:00
zhaojun
52271b5117 fix: 修复目录文档某些情况无法匹配成功的 bug 2022-07-11 14:53:44 +08:00
zhaojun
223a6c1970 去除无用的存储类型枚举 2022-07-11 14:53:04 +08:00
zhaojun
60c272b714 修复 SharePoint 无法直链下载的 bug 2022-07-11 14:52:45 +08:00
zhaojun
3b6ed1c78d fix: 修改数据库和日志等文件默认路径为新版路径 2022-07-10 21:56:43 +08:00
zhaojun
5d888bb68f 🔖 发布 4.0.0 版本 2022-07-10 21:44:14 +08:00
zhaojun
082bb07213 fix: 修复 webdav 账号密码错误问题 2022-06-16 20:11:54 +08:00
zhaojun
803b8cdf71 🔖 发布 3.2.5 版本 2022-05-16 21:41:59 +08:00
zhaojun
eca5f7e48b feature: 新增挂载 webdav client pom 依赖 2022-05-16 21:41:36 +08:00
zhaojun
b70c37f3f0 feature: 新增挂载 webdav 功能 2022-05-16 21:40:58 +08:00
zhaojun
5cb2844141 🐛 修复错误的 content_disposition 和 contentType 导致下载文件格式被浏览器识别错误 2022-05-16 20:50:49 +08:00
赵俊
4dd6cdb4b3 合并拉取请求 #348
fix: modify extract driveId regex in WebDavController
2022-05-03 21:16:40 +08:00
quericy
c6127c029f fix: modify extract driveId regex in WebDavController 2022-04-14 10:06:46 +08:00
zhaojun
e149039ecb 🔖 发布 3.2.3 版本 2022-04-13 00:03:49 +08:00
zhaojun
37688d83cf feature: 降低 webdav 日志级别 2022-04-13 00:03:28 +08:00
zhaojun
6692016642 feature: 新增 webdav 开关功能 2022-04-13 00:01:59 +08:00
zhaojun
3f41aeda9a fix(bug): 修复因字段名称修改升级后导致短链异常 bug 2022-04-12 23:38:49 +08:00
赵俊
de86d5c47d 合并拉取请求 #347
feat: support webdav
2022-04-11 09:56:11 +08:00
quericy
c89e072005 feat: support webdav
1, feat:config webDav and milton in configuration;
2, feat:add event handler in WebDavController;
3, feat:check auth by admin in systemConfig;
4, feat:download webDav file by redirect to site DirectLink;
2022-04-10 04:11:34 +08:00
zhaojun
b533b5e959 🔖 发布 3.2.2 版本 2022-04-02 18:50:50 +08:00
zhaojun
d35cf27d47 fix(bug): ftp 默认为被动模式,防止因端口不同,无法读取。 2022-04-02 18:50:26 +08:00
zhaojun
796c4c1fb0 页面更新 2022-04-02 18:49:26 +08:00
zhaojun
1033d6c1a9 fix(bug): 修复某些情况下直链无法正常使用的 BUG 2022-04-02 18:36:44 +08:00
zhaojun
e09c6b4e58 fix(bug): 修复又拍云文件中包含特殊字符时,调用 upyun 接口未进行 url encode 返回 400,影响又拍云单文件直链无法使用。 2022-04-02 18:26:32 +08:00
zhaojun
168b0b08f3 fix(bug): 修复本地存储可以通过特殊路径符获取任意目录的 BUG. 2022-04-02 18:24:49 +08:00
zhaojun
60a6a5348c fix(bug): 修复获取音频信息时,因文件链接 302, 导致无法正常获取音频文件信息的 BUG. 2022-04-02 18:24:00 +08:00
zhaojun
2ec8a5df1f fix(bug): 修复 sharepoint 未自动刷新 accessToken 和 refreshToken 的 bug 2022-02-02 20:50:13 +08:00
zhaojun
47b5f6ac12 🔖 发布 3.2.1 版本 2022-02-02 20:46:45 +08:00
zhaojun
c64c8465f2 fix(bug): 升级相关有漏洞的依赖版本为最新版 2022-02-02 20:46:08 +08:00
zhaojun
f636681dd8 fix(bug): 修复本地存储可通过特殊命令访问到非指定目录的文件 2022-02-02 20:45:05 +08:00
赵俊
eadd2434e0 新增 ZFILE 社区地址 2021-10-30 21:05:11 +08:00
赵俊
b84c0bff42 Merge remote-tracking branch 'origin/master' 2021-10-07 17:53:19 +08:00
赵俊
4cb5b84bfe 🐛 修复 S3 协议无法正常保存的 BUG 2021-10-07 17:52:45 +08:00
赵俊
0351b4401c Merge pull request #286 from pippen57/master
转义SQL关键字, 防止创建表时出现错误
2021-09-25 18:29:42 +08:00
pippen
48cb14be8a 转义SQL关键字, 防止创建表时出现错误 2021-09-25 12:48:31 +08:00
赵俊
eea2ff11f9 ✏️ 修改文档拼写错误 2021-09-21 11:22:00 +08:00
赵俊
325ec1a348 对于因前端 vue history 路由模式下, 直接访问路径时, 出现的 404 响应码, 转化为 200 响应码. (404 下会将请求转发到首页) 2021-09-20 17:06:27 +08:00
赵俊
93205266d3 更新文档 2021-09-20 16:35:30 +08:00
赵俊
6849a4347f 去除自定义 404 错误页面 2021-09-20 16:33:54 +08:00
赵俊
cb5c6a5945 🎨 提交前端页面 2021-09-20 16:33:31 +08:00
赵俊
5ed45c3bb3 🔒 修复因升级 springboot 版本导致的安全和兼容问题 2021-09-20 16:20:25 +08:00
赵俊
4442ec3165 修复因前端 vue history 路由模式下, 直接访问路径时, 请求 /admin/ 下的路由会被权限框架拦截并校验登陆状态, 导致静态页面报错问题. 2021-09-20 16:19:21 +08:00
赵俊
b268a24333 修复因前端 vue history 路由模式下, 直接访问路径时, 接口和路径名冲突问题 2021-09-20 16:17:59 +08:00
赵俊
84f9354d4e 对于因前端 vue history 路由模式下, 直接访问路径时, 出现的 404 响应码, 转化为 200 响应码. (404 下会将请求转发到首页) 2021-09-20 16:17:16 +08:00
赵俊
8dfc4f8004 跨域请求过滤器修改为自注解注册方式 2021-09-20 16:14:45 +08:00
赵俊
1158f5c2b9 🐛 修复 OneDrive 加速域名在直链下未生效的 BUG 2021-09-20 11:18:13 +08:00
赵俊
c5f0e15207 🐛 修复因升级 spring boot 后部分参数过时, 导致的第一次启动无法初始化参数的 BUG 2021-09-19 21:33:07 +08:00
赵俊
68a842ce75 🐛 修复页面路径和接口地址冲突的 BUG 2021-09-19 16:08:19 +08:00
赵俊
3bd4f74dae 🔖 发布 3.2 版本 2021-09-19 15:37:40 +08:00
赵俊
4a0bdc3baf 💬 修改文档和启动日志描述,取消 /#/ 提示 2021-09-19 15:37:10 +08:00
赵俊
603c1b4654 🎨 提交前端页面 2021-09-19 15:36:19 +08:00
赵俊
1136d582df 增加异常处理器,细化异常提示 2021-09-19 14:10:09 +08:00
赵俊
187544fc06 当密码文件无法正常打开时,给予友好提示 2021-09-19 14:09:52 +08:00
赵俊
aa3cde8f59 minio 和 s3 存储引擎支持自定义 region 功能 2021-09-19 11:30:05 +08:00
赵俊
e29a702b6e ✏️ 拼写错误修改 2021-09-19 11:28:40 +08:00
赵俊
e4f663c9f0 🔒 新增自动检测驱动器已存储参数和驱动器支持所有参数比对功能, 防止驱动器新增参数后, 系统已存在的驱动器因 NPE 问题无法正常加载的问题. 2021-09-19 11:27:57 +08:00
赵俊
fb08ef6e78 🐛 如果请求的密码文件链接是 http 的, 且会自动跳转到 https 时, 密码文件无法正常加载的 BUG (原因是 restTemplate http -> https 不会自动重定向) 2021-09-19 10:01:24 +08:00
赵俊
10c465d159 ♻️ 修改 Spring Security 为 Sa-Token 框架. 2021-09-19 09:57:09 +08:00
赵俊
d15d1203b7 跨域配置 2021-09-19 09:56:12 +08:00
赵俊
de48ed3b61 兼容前端 history 模式, 对于 404 请求, 指向到 /index.html 页面 2021-09-19 09:54:23 +08:00
赵俊
d22e2e872a ⬆️ 因旧版本 Spring 的安全问题, 升级 Spring 为 2.5.4 版本, 兼容新版本 2021-09-19 09:53:19 +08:00
赵俊
7da1b454dc 🐛 修复非个人版的 OneDrive CF 加速域名无法正常使用的 BUG 2021-09-18 22:22:50 +08:00
赵俊
463f311dd3 增加本地文件下载/预览行为控制 2021-07-13 21:43:25 +08:00
赵俊
73b42cf654 🔖 发布 3.1 版本 2021-05-30 17:43:37 +08:00
赵俊
1fac59c4cd 🎨 更新静态页面 2021-05-30 17:41:46 +08:00
赵俊
6ed6b4a019 驱动器添加时,默认 ID 修改为 1 2021-05-30 17:40:48 +08:00
赵俊
7bd02437f0 数据库初始化参数增加默认值 2021-05-30 17:40:14 +08:00
赵俊
57aeb5771c 驱动器列表中返回是否已初始化信息 2021-05-30 17:39:40 +08:00
赵俊
695c03a530 Merge remote-tracking branch 'origin/master' 2021-05-30 16:24:58 +08:00
赵俊
6ebc403572 🔧 添加 ZFile Banner 文件 2021-05-30 16:24:28 +08:00
赵俊
7ff6fe43b5 新增自定义直链名称功能功能 2021-05-30 16:23:41 +08:00
赵俊
b34f141181 🔥 移除无用代码 2021-05-30 16:21:11 +08:00
赵俊
87dd7b58d1 🔥 移除无用代码 2021-05-30 16:19:11 +08:00
赵俊
2a949db5d2 m3u8 响应头兼容部分浏览器直接解析 m3u8 文件 2021-05-30 15:53:55 +08:00
赵俊
b889e91fb5 📝 更新文档,更新赞助信息位置 2021-05-14 21:15:54 +08:00
赵俊
b5c757f9f0 📝 更新文档,更新赞助信息位置 2021-04-21 21:04:02 +08:00
赵俊
a465f48b94 📝 更新文档,增加项目 LOGO,修改项目描述信息。 2021-04-21 20:23:54 +08:00
赵俊
797a0a0e4c 新增直链和短链是否显示功能 2021-04-21 20:21:47 +08:00
赵俊
e7ff159b6d 🐛 修复某些下载地址带密钥的存储策略,m3u8 视频无法正常播放的功能 2021-04-11 19:15:50 +08:00
赵俊
a9fbf54bb2 🔊 完善日志输出 2021-04-11 16:02:49 +08:00
赵俊
81f9e262f5 增加获取站点域名方法 2021-04-11 16:00:01 +08:00
赵俊
23bb3960ab 已关闭的驱动器不允许下载 2021-04-11 15:56:08 +08:00
赵俊
c4a17985a4 优化下载地址获取逻辑,直接列表时不直接调用 API 获取下载地址,访问单文件时再调用。 2021-04-11 15:49:33 +08:00
赵俊
75ddcd47f4 :zip: 优化存储器保存逻辑,防止新增加的字段无法正常保存的 BUG。 2021-04-10 20:21:19 +08:00
赵俊
2dd03ae490 🐛 修复 FTP 模式在 Linux 环境下无法正常读取的 BUG 2021-04-10 20:20:03 +08:00
赵俊
5b383c8741 🐛 日志文件无法正常下载功能修复 2021-04-10 20:19:26 +08:00
赵俊
73198d7852 优化本地文件下载功能, 支持断点续传和多线程下载 2021-04-10 18:06:10 +08:00
赵俊
fb0d9721aa OneDrive 和 SharePoint 反代功能 2021-04-10 15:38:16 +08:00
赵俊
b24c663fd6 更新 star 趋势链接 2021-03-27 12:01:37 +08:00
zhaojun1998
6e62cfc84d 📝 更新文档,修改赞助二维码,增加服务器赞助商链接 2021-03-27 11:52:59 +08:00
zhaojun1998
eee22e9dc9 📝 更新文档 2021-03-26 18:24:02 +08:00
zhaojun1998
5109c51ffc 🔖 发布 3.0 版 2021-03-10 21:43:14 +08:00
zhaojun1998
66d6d311ea 🎨 更新静态页面 2021-03-10 21:42:57 +08:00
zhaojun1998
ef7cbdcbd7 🐛 修复旧版本时,直链多次创建后,根据条件查询时数据不唯一的 BUG 2021-03-10 21:42:36 +08:00
zhaojun1998
63bcbebb4b OneDrive 回调地址增加旧版本兼容 2021-03-10 21:41:42 +08:00
zhaojun1998
50ce1bb6db 修改直链 Key 时检测是否重复功能 2021-03-10 21:41:14 +08:00
zhaojun1998
3c88659679 🎨 更新静态页面 2021-03-09 21:14:42 +08:00
zhaojun1998
d79a993eea 新增设置默认打开图片模式功能 2021-03-09 21:14:21 +08:00
zhaojun1998
afafb311b8 合并文件列表和参数信息接口 2021-03-09 21:13:48 +08:00
zhaojun1998
2e280e4931 新增批量删除直链功能,修改直链 Key 功能 2021-03-08 21:12:42 +08:00
zhaojun1998
ed6efac8b7 🐛 修复切换存储策略时,存储参数值匹配错误的 BUG 2021-03-05 23:00:15 +08:00
zhaojun1998
7409df85d7 🐛 修复切换存储器 ID 时,为级联修改或清理缓存、过滤器、短链的问题 2021-03-05 22:59:08 +08:00
zhaojun1998
4d42529c4d 短链生成逻辑优化, 同链接不重复生成 2021-03-05 22:24:28 +08:00
zhaojun1998
65224685c8 短链生成逻辑优化, 同链接不重复生成 2021-03-05 22:23:41 +08:00
zhaojun1998
080a84986e 💩 优化代码, 添加注释, 优化方法命名, 2021-02-24 18:42:17 +08:00
zhaojun1998
3f8beb2f0b debug 模式优化, 增加到系统参数接口中 2021-02-24 18:40:10 +08:00
zhaojun1998
410a87c426 debug 模式优化, 增加到系统参数接口中 2021-02-24 18:39:47 +08:00
zhaojun1998
c14b8343d2 文件解析异常提示信息优化 2021-02-08 17:58:37 +08:00
zhaojun1998
bea440f6c3 文件解析异常提示信息优化 2021-02-08 17:50:25 +08:00
zhaojun1998
6a5fe15121 密码错误提示信息优化 2021-02-08 17:46:29 +08:00
zhaojun1998
e920ab0ec0 🐛 修复因缓存功能导致密码文件失效的 BUG 2021-02-08 17:46:04 +08:00
zhaojun1998
537e3e0563 🐛 修复 S3 协议新增时的 BUG 2021-02-03 22:01:56 +08:00
赵俊
e208dc7c4c Merge pull request #169 from tianyanli/master
修复S3协议没有指定region问题
2021-02-03 21:56:40 +08:00
zhaojun1998
ed64910a53 🔇 无效的驱动器不输出异常信息,仅返回给页面错误消息 2021-02-03 21:06:38 +08:00
zhaojun1998
5b075c3505 完善 SharePoint 支持 2021-02-03 21:05:46 +08:00
zhaojun1998
a8e6d9af6a 🐛 修复某些存储引擎 API 中文件夹不返回时间和大小时,排序的 NPE 问题。 2021-02-03 21:04:28 +08:00
zhaojun1998
2b21d8a73c 完善 SharePoint 自助获取 SiteId 的逻辑 2021-02-03 21:03:28 +08:00
zhaojun1998
e30289d21b 🔖 发布 2.9.0 版 2021-02-01 23:15:29 +08:00
zhaojun1998
3b6e2be7fe 新增 debug 模式, 可访问数据库控制台和重置密码 2021-02-01 23:14:29 +08:00
zhaojun1998
43c12aa8a7 🎨 更新静态页面 2021-02-01 23:13:29 +08:00
zhaojun1998
c03a7710c0 🎨 更新静态页面 2021-02-01 23:13:18 +08:00
zhaojun1998
1833b23d84 新增 sharepoint 及自助获取 siteId 功能 2021-02-01 23:11:49 +08:00
zhaojun1998
f3e393972d Merge remote-tracking branch 'origin/master' 2021-01-23 23:33:21 +08:00
zhaojun1998
4f46c13369 完善异常处理机制,新增异常类 2021-01-23 13:48:04 +08:00
zhaojun1998
f181959218 支持 SharePoint 2021-01-23 13:46:31 +08:00
zhaojun1998
11effc0ae7 支持自定义 client_id 和 client_secret 2021-01-23 13:45:26 +08:00
zhaojun1998
c8397e17bf 💩 优化代码, 抽取公共方法 2021-01-23 13:44:33 +08:00
zhaojun1998
ed32b9f1d4 限制直链不允许下载密码文件 2021-01-23 13:42:53 +08:00
zhaojun1998
4e184936db 💩 优化代码, 更新注释格式, 去除无用 import 2021-01-23 13:42:14 +08:00
zhaojun1998
fe6aebfdee 自带直链功能 2021-01-23 13:40:16 +08:00
zhaojun1998
d65e1a442d 🔥 移除系统检测功能 2021-01-23 13:36:56 +08:00
zhaojun1998
34647793c8 支持修改驱动器 ID 2021-01-23 13:35:52 +08:00
zhaojun1998
e8c249b9ea 🔥 移除系统检测功能 2021-01-23 13:22:41 +08:00
yanli
d1e613dc10 Update S3ServiceImpl.java 2020-12-17 17:26:55 +08:00
赵俊
1adcfee96f Update README.md 2020-10-07 17:19:26 +08:00
zhaojun1998
75f5de6b9a 🔖 发布 2.8.1 版 2020-08-18 20:43:07 +08:00
zhaojun1998
499942ef70 🎨 更新静态页面 2020-08-18 20:43:02 +08:00
zhaojun1998
e11277ce26 🔖 发布 2.8 版 2020-08-17 21:04:40 +08:00
zhaojun1998
5edd9e38a7 🎨 更新静态页面 2020-08-17 21:04:16 +08:00
zhaojun1998
f4ffee706b 驱动器无效时, 访问系统设置, 给予异常提示 2020-08-16 09:23:07 +08:00
zhaojun1998
bb65750278 查询数据库为空判断 2020-08-16 09:20:21 +08:00
zhaojun1998
e09167c0d0 拖动排序功能 2020-08-15 21:18:38 +08:00
zhaojun1998
ee6c04fa11 🐛 修复 ftp 模式, 依旧获取尝试获取 readme 内容的 BUG. 2020-08-15 17:48:51 +08:00
zhaojun1998
b31982b788 增加文件过滤功能, 同一存储器支持多条规则, 支持通配符. 2020-08-15 17:48:07 +08:00
zhaojun1998
544a3d3eb2 更新静态页面 2020-08-07 21:33:36 +08:00
zhaojun1998
1987bc97a9 🎨 改进 README 文件不存在时, 异常处理机制. 2020-06-27 21:13:24 +08:00
zhaojun1998
7e878af06c 驱动器增加 "启用/禁用" 和 "排序" 功能. 2020-06-27 21:12:54 +08:00
zhaojun1998
766a047ee1 🔊 优化日志输出, 完善日志信息, 便于调试. 2020-06-26 21:35:08 +08:00
zhaojun1998
c1d29a46f5 🎨 代码规范调整, 移除无效引用, 增加注释. 2020-06-26 16:26:09 +08:00
zhaojun1998
08e39b3d15 ✏️ 修复拼写错误 2020-06-26 12:36:35 +08:00
zhaojun1998
e7790ac256 🎨 改进异常处理机制, 给予更详细的提示信息, 便于排查问题. 2020-06-26 12:35:34 +08:00
zhaojun1998
499f3e108c 🔧 日志输出添加代码行号显示, 便于调试. 2020-06-26 12:20:26 +08:00
zhaojun1998
19144b653e 📝 更新 API 文档 2020-06-25 17:54:05 +08:00
zhaojun1998
17a87648fa 🐛 修复本地存储, 填写 Windows 盘符, 无法正常识别的 BUG 2020-06-25 17:52:33 +08:00
zhaojun1998
ac4cef0980 配置文件新增配置项, 以自定义 '日志文件', '数据文件', '临时文件' 的路径 2020-06-25 17:51:35 +08:00
zhaojun1998
71978f8003 🎨 移除未用到的代码 2020-06-25 17:49:58 +08:00
zhaojun1998
0b3a67ec6e 🎨 优化代码结构, 拆分代码 2020-06-25 17:48:38 +08:00
zhaojun1998
ee5fb54ebb 🎨 优化代码结构, 调整包名, 方法名. 2020-06-25 17:05:59 +08:00
zhaojun1998
4585f22817 🔖 发布 2.7 版 2020-05-24 15:46:10 +08:00
zhaojun1998
b523453588 💄 更新页面 2020-05-24 15:45:38 +08:00
zhaojun1998
a8cc03c911 🔨 优化代码 2020-05-24 15:45:25 +08:00
zhaojun1998
949c437653 新增缓存管理功能, 支持手动/自动刷新缓存, 查看、清理缓存。 2020-05-24 15:41:33 +08:00
zhaojun1998
84e9cce60f 🔖 发布 2.6 版 2020-05-13 21:52:46 +08:00
zhaojun1998
5720bd93ec 💄 更新页面 2020-05-13 21:51:52 +08:00
zhaojun1998
bcae9713bc 🐛 修复自定义 JS, CSS 超出数据库长度无法保存的 BUG. 2020-05-13 21:51:27 +08:00
zhaojun1998
04e3023071 🔖 发布 2.5 版 2020-05-04 21:00:33 +08:00
zhaojun1998
a09ef84629 💄 更新页面 2020-05-04 21:00:13 +08:00
zhaojun1998
1d29395191 🐛 修复 OneDrive 某些情况下 Access Token 过长, 因超出数据库长度无法保存的 BUG. 2020-05-04 20:59:48 +08:00
zhaojun1998
3720dc6aa9 🔖 发布 2.4 版 2020-04-20 22:40:06 +08:00
zhaojun1998
3ada172be2 💄 更新页面 2020-04-20 22:39:42 +08:00
zhaojun1998
15f8fbb49b 🐛 修复阿里云腾讯云私有空间时, 密码文件无法加载的 BUG 2020-04-20 22:37:12 +08:00
zhaojun1998
547e688d38 🏗️ 增强系统校验 2020-04-20 21:58:32 +08:00
zhaojun1998
708eb33d0e 🐛 修复多盘中是否多个 OneDrive 导致无法正常加载的 BUG 2020-04-20 21:57:25 +08:00
zhaojun1998
e954b725b1 📝 更新文档 2020-04-19 15:26:04 +08:00
zhaojun1998
c89cb4e495 📝 更新文档 2020-04-19 15:24:36 +08:00
zhaojun1998
286e9775f6 🔧 更新配置文件, 升级后切换默认数据库路径 2020-04-19 14:35:53 +08:00
zhaojun1998
60513abe6a 💄 更新页面, 删除缓存管理模块 2020-04-19 14:35:21 +08:00
zhaojun1998
f7a8c9faa2 🔖 发布 2.3 版 2020-04-19 12:14:27 +08:00
zhaojun1998
55e0d32ef8 🏗️ 架构调整, 支持多个驱动器. 2020-04-19 12:13:12 +08:00
zhaojun1998
4c0bacba31 🔨 重构代码, 将每个存储策略的表单设置改为 Java 配置 2020-04-04 19:57:09 +08:00
zhaojun1998
61128f2677 🎨 移除无用代码 2020-04-04 15:58:33 +08:00
zhaojun1998
3866526b95 📝 补充注释 2020-04-04 15:57:22 +08:00
zhaojun1998
250955fac9 📝 更新文档 2020-04-04 09:50:06 +08:00
zhaojun1998
3249266cd1 🔖 发布 2.2 版 2020-03-08 11:48:48 +08:00
zhaojun1998
1a6235df73 🔨 重构代码, 去除无效引用。 2020-03-08 11:46:31 +08:00
zhaojun1998
430aee2b7f 🔨 重构代码, 去除无效引用。 2020-03-08 11:43:42 +08:00
zhaojun1998
de2f7e4b80 🔨 重构代码, 优化目录结构 2020-03-08 11:11:12 +08:00
zhaojun1998
6dfcc409ac 更新页面 2020-03-08 11:07:01 +08:00
zhaojun1998
78f795e1cb 🔖 发布 2.1 版 2020-03-07 18:39:33 +08:00
zhaojun1998
c69ee0f356 🐛 修复 S3 协议对象存储, 在私有空间下, 无法下载的 BUG. 2020-03-07 13:10:42 +08:00
zhaojun1998
39475de789 🐛 修复 S3 协议对象存储, 在私有空间下, 无法下载的 BUG. 2020-03-07 13:10:21 +08:00
zhaojun1998
bd71712765 🐛 修复无法刷新单个缓存的 BUG 2020-03-07 13:08:42 +08:00
赵俊
8698686a47 Update application.yml 2020-03-01 14:04:14 +08:00
zhaojun1998
762c67ee37 更新文档 2020-02-29 16:04:19 +08:00
zhaojun1998
7bf3a29c17 🔖 发布 2.0 版 2020-02-29 15:48:57 +08:00
zhaojun1998
6ee5002f0c 🔒 修复任意用户名均可登陆后台的安全问题. 2020-02-29 15:47:24 +08:00
zhaojun1998
fadc64add4 💄 更新页面, 修复滚动分页 BUG. 2020-02-29 15:46:56 +08:00
zhaojun1998
234f43846f 优化分页功能 2020-02-29 15:45:34 +08:00
zhaojun1998
67e42d9753 🐛 修复 OneDrive 获取文档区或文件夹密码时, 链接超时导致异常的 BUG. 2020-02-29 15:45:11 +08:00
zhaojun1998
04f94b4bf5 🏗️ 缓存架构调整, 增强稳定性 2020-02-29 15:43:56 +08:00
zhaojun1998
d29c498457 更新配置自描述文件 2020-02-27 23:01:38 +08:00
zhaojun1998
5aa45b44b2 🔖 发布 1.9 版 2020-02-26 21:18:55 +08:00
zhaojun1998
8273a645f2 📝 更新文档 2020-02-26 21:16:49 +08:00
zhaojun1998
46f292cc9b 🔧 更新配置文件, 增加是否开启缓存自动刷新控制 2020-02-26 21:15:45 +08:00
zhaojun1998
261d48059e 增强 FTP 功能, 提高稳定性 2020-02-26 21:14:26 +08:00
zhaojun1998
79f931c51b 增强 OneDrive 调用, 如调用失败, 尝试重新获取 token 后, 再次请求. 2020-02-26 21:14:07 +08:00
zhaojun1998
399e961a65 🏗️ 缓存架构调整, 改为自实现缓存, 移除第三方依赖. 2020-02-26 21:13:08 +08:00
zhaojun1998
3e61d7d146 增加 UFILE 支持 2020-02-26 21:09:42 +08:00
zhaojun1998
ace95d9071 💄 更新页面 2020-02-26 21:09:02 +08:00
zhaojun1998
60d2a2b986 添加 github issue template 2020-02-23 12:07:16 +08:00
zhaojun1998
69d5661e06 添加 github issue template 2020-02-23 12:06:28 +08:00
zhaojun1998
01d11dfc23 移除无用依赖 2020-02-22 15:23:39 +08:00
zhaojun1998
d35e3ecd93 🔖 发布 1.8 版 2020-02-22 15:21:39 +08:00
zhaojun1998
9e5a3e5385 💄 更新页面 2020-02-22 15:18:15 +08:00
zhaojun1998
b62163b4e8 ⬆️ 升级 hutool 依赖版本 2020-02-22 15:16:39 +08:00
zhaojun1998
595a00f067 🐛 修复 FTP 加载 BUG 2020-02-22 15:16:11 +08:00
zhaojun1998
a759d9fe44 增加系统监控及日志下载功能 2020-02-22 12:53:35 +08:00
zhaojun1998
7a24fd10e0 ✏️ 修复拼写错误 2020-02-22 09:35:51 +08:00
zhaojun1998
7e04a817d7 🔖 发布 1.7 版 2020-02-19 21:45:48 +08:00
zhaojun1998
791967f45e 💄 更新页面 2020-02-19 21:43:17 +08:00
zhaojun1998
d789436a16 ✏️ 修复拼写错误 2020-02-19 21:23:09 +08:00
zhaojun1998
96ab8ff7dd 🔖 发布 1.6 版 2020-02-18 21:10:25 +08:00
zhaojun1998
8809aca170 📝 更新页面 2020-02-18 21:08:53 +08:00
zhaojun1998
97106383b6 🐛 修复使用本地存储时, 文件名中包含 + 引发的无法下载的 BUG. 2020-02-18 20:10:20 +08:00
zhaojun1998
208da95234 🐛 修复不同操作系统, 可能出现的编码问题, 导致导入的数据库文件乱码. 2020-02-18 20:09:32 +08:00
zhaojun1998
d4c843f5f5 🐛 修复 OneDrive 文件夹中包含 + 号, 且文件夹中的内容大于 200 个时, 请求出错的 BUG. 2020-02-18 20:02:37 +08:00
zhaojun1998
d273fc9f12 📝 更新文档 2020-02-15 18:39:56 +08:00
zhaojun1998
0b4a38218c 🔖 发布 1.5 版 2020-02-15 18:25:41 +08:00
zhaojun1998
2f57c5b5cc 新增功能: 自定义表格大小, 显示操作按钮, 显示文档, 公告信息 2020-02-15 18:22:37 +08:00
zhaojun1998
610f68295f 📝 更新页面 2020-02-15 18:22:01 +08:00
zhaojun1998
39ced8eb84 📝 更新页面 2020-02-15 18:05:43 +08:00
zhaojun1998
368b7a1df2 优化排序方式为自然排序 2020-02-14 20:22:22 +08:00
zhaojun1998
8e2107cd46 📝 更新开发计划 2020-02-11 18:27:34 +08:00
zhaojun1998
fa32a33371 📝 更新页面 2020-02-09 21:49:27 +08:00
zhaojun1998
59116a9414 🔖 发布 1.4.1 版 2020-02-09 21:47:27 +08:00
zhaojun1998
b2c732a389 🐛 修复 URL 中包含特殊字符, 返回 400 错误的 BUG. 2020-02-09 21:35:25 +08:00
zhaojun1998
0e1ffef92b 🔖 发布 1.4 版 2020-02-09 18:06:50 +08:00
zhaojun1998
a5b19d3577 新增获取直链下载功能. 2020-02-09 18:06:42 +08:00
zhaojun1998
185c84dd79 新增获取直链下载功能. 2020-02-09 18:05:56 +08:00
zhaojun1998
d554dd298c 优化 OneDrive 相关代码结构. 2020-02-09 18:05:01 +08:00
zhaojun1998
aa6ecf0aaa 优化 OneDrive 相关代码结构. 2020-02-09 18:04:33 +08:00
zhaojun1998
83692718e3 📝 更新页面 2020-02-09 18:01:57 +08:00
zhaojun1998
030bd95941 🔖 发布 1.3 版 2020-02-08 17:47:37 +08:00
zhaojun1998
8722d11ac3 📝 更新页面 2020-02-08 17:46:33 +08:00
zhaojun1998
0131ff02c0 🐛 修复头部文件和密码文件, 在开启缓存的状态下, 仅第一次生效的 BUG. 2020-02-08 17:43:02 +08:00
zhaojun1998
2d115bf1c6 更改本地存储, 文件下载时, 文件不存在的响应状态码为 404. 2020-02-08 17:14:01 +08:00
zhaojun1998
946113216d 🔖 发布 1.2.1 版 2020-02-03 20:57:07 +08:00
zhaojun1998
77b05c6dac 🐛 修复 OneDrive 的 Token 某些情况下, 可能超出 2000 字符的 BUG 2020-02-03 20:52:49 +08:00
zhaojun1998
07c9fca210 🔖 发布 1.2 版 2020-02-01 21:54:32 +08:00
zhaojun1998
27cf61693a 更新前端页面, 修复搜索模式下无法进入文件夹和滚动加载的 BUG 2020-02-01 21:54:20 +08:00
zhaojun1998
37e1aa1fec 🐛 修复 OneDrive 文件夹包含特殊符号时, 获取下一页数据 URL 中重复进行了 URL 编码的 BUG. 2020-02-01 21:51:15 +08:00
zhaojun1998
31b54a3c05 调整 OneDrive Token 自动刷新时间为 15 分钟. 2020-02-01 21:50:08 +08:00
zhaojun1998
589f07c103 🔖 发布 1.1 版 2020-01-31 11:52:27 +08:00
zhaojun1998
fe8b60d873 调整日志级别 2020-01-31 11:52:21 +08:00
zhaojun1998
1734619eac 📝 更新页面 2020-01-31 11:50:55 +08:00
zhaojun1998
f5724dc9ab OneDrive 基础路径支持 2020-01-31 11:21:36 +08:00
zhaojun1998
f7bb147b71 📝 更新文档 2020-01-30 19:31:50 +08:00
zhaojun1998
47fc1bc2df 🔖 发布 1.0 版 2020-01-30 18:06:09 +08:00
zhaojun1998
45172f69ba 📝 更新文档 2020-01-30 18:05:56 +08:00
zhaojun1998
9566b138ff 📝 更新页面 2020-01-30 17:58:26 +08:00
zhaojun1998
e15b6c2242 更新配置文件, 增加 h2 console, 便与调试. 2020-01-30 16:52:20 +08:00
zhaojun1998
acc41511e0 S3 协议新增是否为私有空间支持 2020-01-30 16:51:05 +08:00
zhaojun1998
b882b87405 数据库文件初始化更新, 新增 S3 通用协议支持 2020-01-30 16:50:37 +08:00
zhaojun1998
518b5170ae 优化代码 2020-01-30 16:48:37 +08:00
zhaojun1998
8bfac6d9ac 新增 S3 协议通用支持 2020-01-30 16:47:58 +08:00
zhaojun1998
3ffdb4f1b2 清洁代码 2020-01-30 16:47:03 +08:00
zhaojun1998
47509450a0 📝 更新文档, 修改 Debian/Ubuntu 安装命令. 2020-01-29 18:47:38 +08:00
zhaojun1998
812fd18aac 🐛 修复 OneDrive 世纪互联版自动刷新 REFRESH_TOKEN 失败的 BUG 2020-01-29 16:24:00 +08:00
zhaojun1998
77a13cf8ad 📝 更新文档, 新增预览图片 2020-01-29 13:56:20 +08:00
zhaojun1998
4c914793b0 📝 更新文档 2020-01-29 13:43:19 +08:00
zhaojun1998
5698cfb2d3 🔖 发布 0.9 版 2020-01-29 13:41:42 +08:00
zhaojun1998
3cd5f8f9a7 调整显示顺序 2020-01-29 12:58:03 +08:00
zhaojun1998
76747771de OneDrive 世纪互联支持 2020-01-29 12:53:38 +08:00
zhaojun1998
cfacd39210 优化代码, 增强健壮性 2020-01-29 12:52:07 +08:00
zhaojun1998
90cd13f2c3 🔖 发布 0.8 版 2020-01-28 15:19:04 +08:00
zhaojun1998
018a68246e 💄 更新页面 2020-01-28 15:16:27 +08:00
zhaojun1998
b6a2e3ccb8 添加获取指定路径文件信息的 API 2020-01-28 14:55:06 +08:00
zhaojun1998
38b811f8e6 🐛 新增 '搜索包含加密文件' 支持. 2020-01-28 13:25:28 +08:00
zhaojun1998
6922fa2195 添加自定义 JS, CSS 支持 2020-01-28 13:14:23 +08:00
zhaojun1998
4bca6cf7a5 移除尾部说明文件支持 2020-01-28 13:13:52 +08:00
zhaojun1998
c3484426ab 🐛 修复 OneDrive 教育版和部分国际版无法正常获取文件的 BUG 2020-01-28 10:18:36 +08:00
zhaojun1998
0455bd366c 🔖 发布 0.7.1 版 2020-01-26 13:42:19 +08:00
zhaojun1998
bbe3c053f8 OneDrive 回调页面友好提示 2020-01-26 13:41:15 +08:00
zhaojun1998
f47708f45d Merge remote-tracking branch 'origin/master' 2020-01-26 13:38:24 +08:00
zhaojun1998
2e7a7b8cec 🐛 修复文件夹包含特殊字符编码 BUG 2020-01-26 13:35:50 +08:00
zhaojun1998
9e067dbce9 🐛 修复国际版 OneDrive, 无法获取子目录的 BUG 2020-01-26 13:35:27 +08:00
zhaojun1998
a4a236e488 🐛 修复 OneDrive 列目录, 文件数超出 200 个无法显示的 BUG. 2020-01-26 13:21:49 +08:00
赵俊
7d5b0431f5 Update README.md 2020-01-25 18:10:22 +08:00
zhaojun1998
a758c8cc6d 📝 更新文档 2020-01-25 18:06:13 +08:00
zhaojun1998
21a64ec0f3 🐛 修复循环依赖 BUG 2020-01-25 18:00:38 +08:00
zhaojun1998
3f241d129a 📝 更新文档 2020-01-25 18:00:14 +08:00
zhaojun1998
fa5f16c61f 🔖 发布 0.7 版 2020-01-24 19:00:56 +08:00
zhaojun1998
492b22506d 🐛 修复切换存储策略时没有重新缓存的 BUG 2020-01-24 18:51:20 +08:00
zhaojun1998
a12f685340 🐛 修复 FTP 初始化显示错误 2020-01-24 18:44:59 +08:00
zhaojun1998
2ee3f3dd66 💄 更新页面 2020-01-24 18:39:20 +08:00
zhaojun1998
245937e773 搜索忽略大小写支持 2020-01-24 18:37:58 +08:00
zhaojun1998
aef34facbd 🐛 修复更改系统设置时, 误清理缓存的 BUG 2020-01-24 18:37:32 +08:00
zhaojun1998
14bb5e15e3 替换 onedrive 授权重定向地址, 配置文件添加元数据描述 2020-01-24 17:40:01 +08:00
zhaojun1998
12371f06dd 🐛 修复 S3 协议中, 某些情况下出现了空文件名的 BUG 2020-01-24 17:28:48 +08:00
zhaojun1998
28e43e968f 添加分页, 修复本地存储下载错误 2020-01-24 17:27:12 +08:00
zhaojun1998
669b413ff0 优化存储策略功能, 添加是否初始化成功标识 2020-01-24 17:13:49 +08:00
zhaojun1998
f32e5e8f9e 🐛 修复切换存储引擎时, 没有清空原引擎缓存的 BUG. 2020-01-24 10:46:00 +08:00
zhaojun1998
3719378614 ✏️ 修复拼写错误 2020-01-24 10:28:21 +08:00
zhaojun1998
40c759078e 🐛 MINIO 修改 URL 路径风格指定为 path-style, 防止配置域名情况下, 找不到域名的 BUG. 2020-01-24 10:27:53 +08:00
zhaojun1998
e37e778e1a 优化日志输出 2020-01-23 10:53:16 +08:00
zhaojun1998
031607402a 本地存储, 文件不存在时, 给与友好提示 2020-01-20 22:48:04 +08:00
zhaojun1998
6c9150466c 优化日志配置 2020-01-20 21:36:58 +08:00
zhaojun1998
be633ebe1a OneDrive 支持 2020-01-20 21:36:13 +08:00
zhaojun1998
9715cf922a 优化日志输出 2020-01-20 21:35:41 +08:00
zhaojun1998
f6163c7e19 优化代码结构 2020-01-20 21:35:20 +08:00
zhaojun1998
dcc4cb19ad OneDrive 支持 2020-01-19 21:58:02 +08:00
zhaojun1998
ad0ad12c08 搜索功能支持分页 2020-01-18 22:57:29 +08:00
zhaojun1998
74c935cdf0 抽取通用代码 2020-01-17 23:28:23 +08:00
zhaojun1998
1876e692f2 💄 更改默认排序器 2020-01-16 22:24:46 +08:00
zhaojun1998
f198b34324 🔖 升级版本为 0.6 2020-01-12 11:18:50 +08:00
zhaojun1998
3095e0c8d9 💄 更新页面, 优化缓存页面展示 2020-01-12 11:15:01 +08:00
zhaojun1998
594246127d 🐛 修复更改策略时, 未正确启用和关闭缓存的 BUG 2020-01-12 11:14:20 +08:00
zhaojun1998
f6c5f7a91b 🔧 修改配置配置文件, 将日志改为单独配置 2020-01-12 11:13:02 +08:00
zhaojun1998
2a765fff7e 🐛 获取缓存是否开启时的 NPE 问题 2020-01-11 23:20:12 +08:00
zhaojun1998
28f958878b 🔊 更新日志级别 2020-01-11 23:02:19 +08:00
zhaojun1998
368f3a90eb 移除无用依赖 2020-01-11 22:53:19 +08:00
zhaojun1998
98b14abbfc 🔧 修改配置文件, 去除默认缓存时间. 2020-01-11 22:49:55 +08:00
zhaojun1998
7c04c3d6b8 优化代码结构 2020-01-11 22:48:28 +08:00
zhaojun1998
921cb1a115 通过反射优化代码 2020-01-08 22:34:15 +08:00
zhaojun1998
9371968c3b 增强根据 value 获取枚举的功能 2020-01-08 22:33:30 +08:00
zhaojun1998
47e88849ac 🐛 修复当前存储引擎为空时, 与新设置的存储引擎比较出现的 NPE 2020-01-08 22:32:47 +08:00
zhaojun1998
2f0f41f413 去除无效输出 2020-01-08 21:31:24 +08:00
zhaojun1998
7667765abc 🐛 修复本地存储的文件名, 包含 &?=[] 等特殊字符时出现的问题 2020-01-08 21:28:05 +08:00
zhaojun1998
b2a2e69af5 🔒 关闭 URL 部分校验, 允许中文文件名 2020-01-08 21:22:03 +08:00
zhaojun1998
7c729a72e2 🐛 本地存储, 获取文件不存在返回状态码 404 2020-01-07 22:57:59 +08:00
zhaojun1998
5495abc881 更新未知歌曲默认封面 2020-01-06 23:00:07 +08:00
zhaojun1998
797cd4fc06 💄 更新页面 2020-01-05 16:11:02 +08:00
zhaojun1998
8148d182cf 更新缓存策略刷新时间 2020-01-05 16:01:50 +08:00
zhaojun1998
7e8cab90d0 🔖 升级版本为 0.5 2020-01-05 15:58:46 +08:00
zhaojun1998
4d5743dc0b 💄 更新页面 2020-01-05 15:56:51 +08:00
zhaojun1998
1a326cc17d 💄 更新页面 2020-01-05 15:56:38 +08:00
zhaojun1998
cc993d8e65 优化缓存策略, 开启/关闭缓存后同步控制自动刷新策略 2020-01-05 15:56:17 +08:00
zhaojun1998
f128882034 添加文件获取和判断是否存在接口 2020-01-05 15:54:59 +08:00
zhaojun1998
31dbb902c3 添加文件获取和判断是否存在接口 2020-01-05 15:54:42 +08:00
zhaojun1998
c849057aaa 💄 更新页面 2020-01-03 17:00:15 +08:00
zhaojun1998
7b288b795c 🐛 修复切换缓存时, 出现的异常 BUG 2020-01-03 16:24:53 +08:00
zhaojun1998
316566d479 📝 更新文档, 修复拼写错误 2020-01-03 16:16:05 +08:00
zhaojun1998
e01ce28eb8 💄 更新页面 2020-01-03 15:59:41 +08:00
zhaojun1998
9b7528b61c 💄 更新页面 2020-01-03 15:49:22 +08:00
zhaojun1998
bd22cfd34c 添加检测缓存管理功能 2020-01-03 15:48:42 +08:00
zhaojun1998
4aa9839c6b 🔖 升级版本为 0.4 2020-01-03 15:28:58 +08:00
zhaojun1998
5eeea23703 缓存功能优化, 更高效的管理缓存. 2020-01-03 15:27:45 +08:00
zhaojun1998
6997b15dd0 修复系统设置缓存读取到远程的 BUG 2020-01-02 18:45:19 +08:00
zhaojun1998
326c954c36 💄 更新页面 2020-01-02 18:44:36 +08:00
zhaojun1998
ba5801bea2 🔖 升级版本为 0.3.1 2020-01-02 18:27:30 +08:00
zhaojun1998
e2b0c29e2d 更改日志级别 2020-01-02 18:23:24 +08:00
zhaojun1998
845a380a7e 未开启缓存, 不允许搜索. 2020-01-02 18:22:54 +08:00
zhaojun1998
87229f225e 系统配置添加缓存, 优化性能 2020-01-02 18:20:06 +08:00
zhaojun1998
de947e510c 缓存功能优化, 修改设置后自动修改缓存, 且缓存未完成, 搜索功能暂时禁用. 2020-01-02 18:17:11 +08:00
zhaojun1998
2a367afc37 优化代码, 参数不完整时, 不进行初始化 2020-01-02 18:15:05 +08:00
821 changed files with 24730 additions and 3716 deletions

24
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,24 @@
---
name: BUG 反馈
about: 事情不像预期的那样工作吗?
title: ''
labels: 'bug'
assignees: ''
---
为了帮助我们更好的解决您的问题,请填写以下选项(不填写完整可能会被直接关闭 issue
- 是否已搜索其他 issue没有人提过这个问题
- 当前 ZFile 版本:
- 是否尝试最新版是否已解决此问题:
- 是否尝试重启 ZFile且问题依旧存在
- 是否已尝试清空浏览器缓存,且问题依旧存在?:
- 操作系统(如 Windows、Mac、iOS、安卓
- 浏览器(如 Chrome、Firefox、SafariX 浏览器):
- 做什么操作提示的错误?:
- 期望行为(应该是什么样的结果):
- 当前行为(当前是什么样的结果):
- 错误日志(可选):
- 复现步骤(可选):
- 您的额外信息(可选):

View File

@@ -0,0 +1,17 @@
---
name: 功能建议
about: 想让我们为 ZFile 增加什么功能吗?
title: 'feat: '
labels: 'Feature Request'
assignees: ''
---
为了帮助我们更好的解决您的问题,请填写以下选项(不填写完整可能会被直接关闭 issue
- 是否已搜索其他 issue没有人提过这个功能
- 是否已尝试使用最新版本,且仍然没有此功能?:
- 功能概述:
- 功能动机:
- 详细解释(可选):

15
.gitignore vendored
View File

@@ -1,11 +1,8 @@
HELP.md
target/
.mvn/wrapper/**
!**/src/main/**
**/src/test/**
mvnw
mvnw.cmd
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
### STS ###
.apt_generated
@@ -29,6 +26,12 @@ mvnw.cmd
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/
/.mvn/wrapper/
/mvnw
/mvnw.cmd
/.script/

148
README.md
View File

@@ -1,105 +1,77 @@
# Z-File
<p align="center">
![https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square](https://img.shields.io/badge/license-MIT-blue.svg?longCache=true&style=flat-square)
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/70b793267f7941d58cbd93f50c9a8e0a)](https://www.codacy.com/manual/zhaojun1998/zfile?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=zhaojun1998/zfile&amp;utm_campaign=Badge_Grade)
![https://img.shields.io/badge/springboot-2.0.6-orange.svg?style=flat-square](https://img.shields.io/badge/springboot-2.0.6-yellow.svg?longCache=true&style=popout-square)
![GitHub tag (latest SemVer)](https://img.shields.io/github/tag/zhaojun1998/zfile.svg?style=flat-square)
![zfile](https://cdn.jun6.net/uPic/2022/09/04/zfile-header.png)
此项目是一个在线文件目录的程序, 支持各种对象存储和本地存储, 使用定位是个人放常用工具下载, 或做公共的文件库. 不会向多账户方向开发.
前端基于 [h5ai](https://larsjung.de/h5ai/) 的原有功能使用 Vue 重新开发了一遍. 后端采用 SpringBoot, 数据库采用内嵌数据库.
预览地址: [http://zfile.jun6.net](http://zfile.jun6.net)
## 系统特色
* 内存缓存 (免安装)
* 内存数据库 (免安装)
* 个性化配置
* 自定义目录的 header 和 footer 说明文件
* 文件夹密码
* 支持在线浏览文本文件, 视频, 图片, 音乐.
* 文件/目录二维码
* 缓存动态开启, 缓存自动刷新
* 全局搜索
基于 Java 的在线网盘程序,支持对接 S3、OneDrive、SharePoint、又拍云、本地存储、FTP、SFTP 等存储源支持在线浏览图片、播放音视频文本文件、Office、obj3d等文件类型。
<br><br>
<img src="https://img.shields.io/badge/license-MIT-blue.svg?longCache=true&style=flat-square" alt="license">
<img src="https://api.codacy.com/project/badge/Grade/70b793267f7941d58cbd93f50c9a8e0a" alt="codady">
<img src="https://img.shields.io/github/last-commit/zhaojun1998/zfile.svg?style=flat-square" alt="last commit">
<img src="https://img.shields.io/github/downloads/zhaojun1998/zfile/total?style=flat-square" alt="downloads">
<img src="https://img.shields.io/github/v/release/zhaojun1998/zfile?style=flat-square" alt="release">
<img src="https://img.shields.io/github/commit-activity/y/zhaojun1998/zfile?style=flat-square" alt="commit activity">
<br>
<img src="https://img.shields.io/github/issues/zhaojun1998/zfile?style=flat-square" alt="issues">
<img src="https://img.shields.io/github/issues-closed-raw/zhaojun1998/zfile?style=flat-square" alt="closed issues">
<img src="https://img.shields.io/github/forks/zhaojun1998/zfile?style=flat-square" alt="forks">
<img src="https://img.shields.io/github/stars/zhaojun1998/zfile?style=flat-square" alt="stars">
<img src="https://img.shields.io/github/watchers/zhaojun1998/zfile?style=flat-square" alt="watchers">
</p>
## 快速开始
安装 JDK 1.8 :
请参考部署文档: [https://docs.zfile.vip](https://docs.zfile.vip)
```bash
yum instal -y java # 适用于 Centos 7.x
```
## 在线体验
下载项目:
[https://demo.zfile.vip](https://demo.zfile.vip)
```bash
wget https://github.com/zhaojun1998/zfile/releases/download/0.3/zfile-0.3.jar
```
## 功能预览
启动项目:
```bash
java -Djava.security.egd=file:/dev/./urandom -jar zfile-0.3.jar
## 高级启动
java -Djava.security.egd=file:/dev/./urandom -jar zfile-0.3.jar --server.port=18777
## 后台运行
nohup java -Djava.security.egd=file:/dev/./urandom -jar zfile-0.3.jar &
```
> 系统使用的是内置配置文件, 默认配置请参考: [application.yml](https://github.com/zhaojun1998/zfile/blob/master/src/main/resources/application.yml)
> **可下载此文件放置与 jar 包同目录, 此时会以外部配置文件为准, 推荐适用此方式!.**
> 所有参数都可在命令行启动时, 以类似 `--server.port=18777` 的方式强制执行, 此方式的优先级最高.
> *指定 `-Djava.security.egd=file:/dev/./urandom` 是为了防止在 Linux 环境中, 生成首次登陆生成 sessionId 取系统随机数过慢的问题.*
重要参数:
- `server.port` 为指定端口, 默认为 `8080`
- `logging.path` 为日志文件存放路径, 默认为 `${user.home}/.zfile/logs`
- `spring.datasource` 下配置了 `h2``mysql` 两种数据库的支持, 默认采用 `h2`.
- `spring.cache.type` 为指定缓存方式, 默认为 `caffeine`, 即内存缓存, 无需安装, 支持切换为 `redis`, 但需配置 `spring.redis.host``spring.redis.password` 参数后才可使用.
### 文件列表
![文件列表](https://cdn.jun6.net/uPic/2022/08/13/0urMn8.png)
### 画廊模式
![图片预览](https://cdn.jun6.net/uPic/2022/08/13/d2J9aE.png)
### 视频预览
![视频预览](https://cdn.jun6.net/uPic/2022/08/13/tBX00R.png)
### 文本预览
![文本预览](https://cdn.jun6.net/uPic/2022/08/13/7dDy4G.png)
### 音频预览
![音频预览](https://cdn.jun6.net/uPic/2022/08/13/N5bU1R.png)
### PDF 预览
![PDF 预览](https://cdn.jun6.net/uPic/2022/08/13/H327bV.png)
### Office 预览
![Office 预览](https://cdn.jun6.net/uPic/2022/08/27/RxeiqI.png)
### 3d 文件预览
![3d 文件预览](https://cdn.jun6.net/uPic/2022/08/29/8iszyh.png)
### 生成直链
![生成直链](https://cdn.jun6.net/uPic/2022/08/13/zCX3xT.jpg)
### 页面设置
![页面设置](https://cdn.jun6.net/uPic/2022/08/13/54nYv2.png)
### 后台设置-登录
![后台设置-登录](https://cdn.jun6.net/uPic/2022/08/13/J8P2Zf.png)
### 后台设置-存储源列表
![后台设置-存储源列表](https://cdn.jun6.net/uPic/2022/08/13/jymieO.png)
### 后台设置-存储源权限控制
![后台设置-存储源权限控制](https://cdn.jun6.net/uPic/2022/08/13/JgiwkH.jpg)
### 后台设置-添加存储源(本地存储)
![后台设置-添加存储源(本地存储)](https://cdn.jun6.net/uPic/2022/08/13/add-storage.png)
### 后台设置-添加存储源(世纪互联)
![后台设置-添加存储源(世纪互联)](https://cdn.jun6.net/uPic/2022/08/13/add-storage2.png)
### 后台设置-显示设置
![后台设置-显示设置](https://cdn.jun6.net/uPic/2022/08/13/view-setting.png)
访问地址:
## 支持作者
用户前台: http://127.0.0.1:8080/#/main
如果本项目对你有帮助,请作者喝杯咖啡吧。
初始安装: http://127.0.0.1:8080/#/install
<img src="https://cdn.jun6.net/2021/03/27/152704e91f13d.png" width="400" alt="赞助我">
管理后台: http://127.0.0.1:8080/#/admin
## Status
![Alt](https://repobeats.axiom.co/api/embed/580333f83b91087e713f15497e6433c50e1da090.svg "Repobeats analytics image")
## 运行环境
## Star History
* JDK: `1.8`
* 缓存: `caffeine`
* 数据库: `h2/mysql`
## 常见问题
### 数据库
缓存默认支持 `h2``mysql`, 前者为嵌入式数据库, 无需安装, 但后者相对性能更好.
### 默认路径
默认 H2 数据库文件地址: `~/.zfile/db/`, `~` 表示用户目录, windows 为 `C:/Users/用户名/`, linux 为 `/home/用户名/`, root 用户为 `/root/`
### 头尾文件和加密文件
- 目录头部显示文件名为 `header.md`
- 目录底部显示文件名为 `footer.md`
- 目录需要密码访问, 添加文件 `password.txt` (无法拦截此文件被下载, 但可以改名文件)
## TODO
- 文本预览更换更好用的编辑器
- 后台支持上传、编辑、删除等操作
- API 支持
- 更方便的部署方式
[![Star History Chart](https://api.star-history.com/svg?repos=zfile-dev/zfile&type=Date)](https://star-history.com/#zfile-dev/zfile&Date)

231
pom.xml
View File

@@ -1,27 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.6.RELEASE</version>
<relativePath/>
<version>2.6.8</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>im.zhaojun</groupId>
<artifactId>zfile</artifactId>
<version>0.3</version>
<version>4.1.2</version>
<name>zfile</name>
<packaging>war</packaging>
<description>一个在线的文件浏览系统</description>
<properties>
<java.version>1.8</java.version>
<org.mapstruct.version>1.5.1.Final</org.mapstruct.version>
</properties>
<dependencies>
<!-- spring boot 官方相关 -->
<dependency>
<groupId>org.springframework.boot</groupId>
@@ -31,41 +31,49 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!-- 数据库驱动-->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!-- 数据库相关 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.29</version>
<scope>runtime</scope>
</dependency>
<!-- 工具类 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>4.5.11</version>
<groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
<version>7.15.0</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>
<!-- 存储引擎相关 API, 对象存储、FTP、 Rest API-->
<!-- 存储策略相关 SDK、 工具类-->
<dependency>
<groupId>com.upyun</groupId>
<artifactId>java-sdk</artifactId>
@@ -74,36 +82,110 @@
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-s3</artifactId>
<version>1.11.699</version>
<version>1.12.261</version>
</dependency>
<dependency>
<groupId>com.qiniu</groupId>
<artifactId>qiniu-java-sdk</artifactId>
<version>7.11.0</version>
</dependency>
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.55</version>
</dependency>
<dependency>
<groupId>com.github.lookfirst</groupId>
<artifactId>sardine</artifactId>
<version>5.10</version>
</dependency>
<!-- 登陆/权限相关 -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-starter</artifactId>
<version>1.30.0</version>
</dependency>
<!-- 文档相关 -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
<!-- 工具类 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.3</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.8</version>
</dependency>
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>3.6</version>
<version>3.8.0</version>
</dependency>
<!-- 其他工具类 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
</dependency>
<dependency>
<groupId>com.mpatric</groupId>
<artifactId>mp3agic</artifactId>
<version>0.9.1</version>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.83</version>
</dependency>
<dependency>
<groupId>com.alicp.jetcache</groupId>
<artifactId>jetcache-starter-redis</artifactId>
<version>2.5.14</version>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>30.1.1-jre</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>
<dependency>
<groupId>commons-chain</groupId>
<artifactId>commons-chain</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>dev.samstevens.totp</groupId>
<artifactId>totp-spring-boot-starter</artifactId>
<version>1.7.1</version>
</dependency>
<dependency>
<groupId>com.beust</groupId>
<artifactId>jcommander</artifactId>
<version>1.82</version>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20200518</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
<version>4.5.13</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.70</version>
</dependency>
</dependencies>
@@ -113,8 +195,69 @@
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
<annotationProcessorPaths>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-mapstruct-binding</artifactId>
<version>0.2.0</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
<plugin>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>com.uyoqu.framework</groupId>
<artifactId>maven-plugin-starter</artifactId>
<version>1.0.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>bin</goal>
</goals>
</execution>
</executions>
<configuration>
<jvms>
<jvm>-Djava.security.egd=file:/dev/./urandom</jvm>
<jvm>-Dfile.encoding=utf-8</jvm>
<jvm>-Djava.net.preferIPv4Stack=false</jvm>
<jvm>-Djava.net.preferIPv4Addresses=true</jvm>
<jvm>-Djava.awt.headless=true</jvm>
</jvms>
</configuration>
</plugin>
</plugins>
</build>
</project>
</build>
</project>

View File

@@ -0,0 +1,173 @@
/*
* Copyright (c) 2011-2022, baomidou (jobob@qq.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.baomidou.mybatisplus.core.handlers;
import com.baomidou.mybatisplus.annotation.EnumValue;
import com.baomidou.mybatisplus.annotation.IEnum;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.ExceptionUtils;
import com.baomidou.mybatisplus.core.toolkit.ReflectionKit;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaClass;
import org.apache.ibatis.reflection.ReflectorFactory;
import org.apache.ibatis.reflection.invoker.Invoker;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
/**
* 自定义枚举属性转换器
*
* @author hubin
* @since 2017-10-11
*/
public class MybatisEnumTypeHandler<E extends Enum<E>> extends BaseTypeHandler<E> {
private static final Map<String, String> TABLE_METHOD_OF_ENUM_TYPES = new ConcurrentHashMap<>();
private static final ReflectorFactory REFLECTOR_FACTORY = new DefaultReflectorFactory();
private final Class<E> enumClassType;
private final Class<?> propertyType;
private final Invoker getInvoker;
public MybatisEnumTypeHandler(Class<E> enumClassType) {
if (enumClassType == null) {
throw new IllegalArgumentException("Type argument cannot be null");
}
this.enumClassType = enumClassType;
MetaClass metaClass = MetaClass.forClass(enumClassType, REFLECTOR_FACTORY);
String name = "value";
if (!IEnum.class.isAssignableFrom(enumClassType)) {
name = findEnumValueFieldName(this.enumClassType).orElseThrow(() -> new IllegalArgumentException(String.format("Could not find @EnumValue in Class: %s.", this.enumClassType.getName())));
}
this.propertyType = ReflectionKit.resolvePrimitiveIfNecessary(metaClass.getGetterType(name));
this.getInvoker = metaClass.getGetInvoker(name);
}
/**
* 查找标记标记EnumValue字段
*
* @param clazz class
* @return EnumValue字段
* @since 3.3.1
*/
public static Optional<String> findEnumValueFieldName(Class<?> clazz) {
if (clazz != null && clazz.isEnum()) {
String className = clazz.getName();
return Optional.ofNullable(CollectionUtils.computeIfAbsent(TABLE_METHOD_OF_ENUM_TYPES, className, key -> {
Optional<Field> fieldOptional = findEnumValueAnnotationField(clazz);
return fieldOptional.map(Field::getName).orElse(null);
}));
}
return Optional.empty();
}
private static Optional<Field> findEnumValueAnnotationField(Class<?> clazz) {
return Arrays.stream(clazz.getDeclaredFields()).filter(field -> field.isAnnotationPresent(EnumValue.class)).findFirst();
}
/**
* 判断是否为MP枚举处理
*
* @param clazz class
* @return 是否为MP枚举处理
* @since 3.3.1
*/
public static boolean isMpEnums(Class<?> clazz) {
return clazz != null && clazz.isEnum() && (IEnum.class.isAssignableFrom(clazz) || findEnumValueFieldName(clazz).isPresent());
}
@SuppressWarnings("Duplicates")
@Override
public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType)
throws SQLException {
if (jdbcType == null) {
ps.setObject(i, this.getValue(parameter));
} else {
// see r3589
ps.setObject(i, this.getValue(parameter), jdbcType.TYPE_CODE);
}
}
@Override
public E getNullableResult(ResultSet rs, String columnName) throws SQLException {
Object value = rs.getObject(columnName);
if (null == value && rs.wasNull()) {
return null;
}
return this.valueOf(value);
}
@Override
public E getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
Object value = rs.getObject(columnIndex, this.propertyType);
if (null == value && rs.wasNull()) {
return null;
}
return this.valueOf(value);
}
@Override
public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
Object value = cs.getObject(columnIndex, this.propertyType);
if (null == value && cs.wasNull()) {
return null;
}
return this.valueOf(value);
}
private E valueOf(Object value) {
E[] es = this.enumClassType.getEnumConstants();
return Arrays.stream(es).filter((e) -> equalsValue(value, getValue(e))).findAny().orElse(null);
}
/**
* 值比较
*
* @param sourceValue 数据库字段值
* @param targetValue 当前枚举属性值
* @return 是否匹配
* @since 3.3.0
*/
protected boolean equalsValue(Object sourceValue, Object targetValue) {
String sValue = StringUtils.toStringTrim(sourceValue);
String tValue = StringUtils.toStringTrim(targetValue);
if (sourceValue instanceof Number && targetValue instanceof Number
&& new BigDecimal(sValue).compareTo(new BigDecimal(tValue)) == 0) {
return true;
}
return Objects.equals(sValue, tValue);
}
private Object getValue(Object object) {
try {
return this.getInvoker.invoke(object, new Object[0]);
} catch (ReflectiveOperationException e) {
throw ExceptionUtils.mpe(e);
}
}
}

View File

@@ -1,24 +0,0 @@
package im.zhaojun;
import com.alicp.jetcache.anno.config.EnableCreateCacheAnnotation;
import com.alicp.jetcache.anno.config.EnableMethodCache;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.scheduling.annotation.EnableAsync;
/**
* @author zhaojun
*/
@EnableAsync
@SpringBootApplication
@EnableMethodCache(basePackages = "im.zhaojun", proxyTargetClass = true)
@EnableCreateCacheAnnotation
@EnableAspectJAutoProxy(exposeProxy = true)
public class ZfileApplication {
public static void main(String[] args) {
SpringApplication.run(ZfileApplication.class, args);
}
}

View File

@@ -1,54 +0,0 @@
package im.zhaojun.aliyun.service;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.client.builder.AwsClientBuilder;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import im.zhaojun.common.model.StorageConfig;
import im.zhaojun.common.model.constant.StorageConfigConstant;
import im.zhaojun.common.model.enums.StorageTypeEnum;
import im.zhaojun.common.service.AbstractS3FileService;
import im.zhaojun.common.service.FileService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import java.util.Map;
/**
* @author zhaojun
*/
@Service
public class AliyunServiceImpl extends AbstractS3FileService implements FileService {
private static final Logger log = LoggerFactory.getLogger(AliyunServiceImpl.class);
@Override
public void init() {
try {
Map<String, StorageConfig> stringStorageConfigMap = storageConfigService.selectStorageConfigMapByKey(StorageTypeEnum.ALIYUN);
String accessKey = stringStorageConfigMap.get(StorageConfigConstant.ACCESS_KEY).getValue();
String secretKey = stringStorageConfigMap.get(StorageConfigConstant.SECRET_KEY).getValue();
String endPoint = stringStorageConfigMap.get(StorageConfigConstant.ENDPOINT_KEY).getValue();
super.bucketName = stringStorageConfigMap.get(StorageConfigConstant.BUCKET_NAME_KEY).getValue();
super.domain = stringStorageConfigMap.get(StorageConfigConstant.DOMAIN_KEY).getValue();
super.basePath = stringStorageConfigMap.get(StorageConfigConstant.BASE_PATH).getValue();
BasicAWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey);
super.s3Client = AmazonS3ClientBuilder.standard()
.withCredentials(new AWSStaticCredentialsProvider(credentials))
.withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(endPoint, "oss")).build();
isInitialized = testConnection();
} catch (Exception e) {
log.debug(getStorageTypeEnum().getDescription() + "初始化异常, 已跳过");
}
}
@Override
public StorageTypeEnum getStorageTypeEnum() {
return StorageTypeEnum.ALIYUN;
}
}

View File

@@ -1,27 +0,0 @@
package im.zhaojun.common.aspect;
import im.zhaojun.common.exception.StorageStrategyUninitializedException;
import im.zhaojun.common.service.AbstractFileService;
import im.zhaojun.common.service.SystemConfigService;
import im.zhaojun.common.util.SpringContextHolder;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
/**
* @author zhaojun
*/
@Aspect
@Component
public class StorageStrategyInitCheckAspect {
@Before("@annotation(im.zhaojun.common.annotation.CheckStorageStrategyInit)")
public void logStart() {
SystemConfigService systemConfigService = SpringContextHolder.getBean(SystemConfigService.class);
AbstractFileService currentFileService = systemConfigService.getCurrentFileService();
if (currentFileService == null || !currentFileService.getIsInitialized()) {
throw new StorageStrategyUninitializedException("存储策略异常, 请联系管理员!");
}
}
}

View File

@@ -1,50 +0,0 @@
package im.zhaojun.common.config;
import im.zhaojun.common.model.enums.StorageTypeEnum;
import im.zhaojun.common.service.AbstractFileService;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* @author zhaojun
*/
@Component
public class StorageTypeFactory implements ApplicationContextAware {
private static Map<String, AbstractFileService> storageTypeEnumFileServiceMap;
private static ApplicationContext applicationContext;
/**
* 项目启动时执行
*/
@Override
public void setApplicationContext(ApplicationContext act) throws BeansException {
applicationContext = act;
// 获取 Spring 容器中所有 FileService 类型的类
storageTypeEnumFileServiceMap = act.getBeansOfType(AbstractFileService.class);
}
/**
* 获取指定存储类型 Service
*/
public static AbstractFileService getStorageTypeService(StorageTypeEnum type) {
AbstractFileService result = null;
for (AbstractFileService fileService : storageTypeEnumFileServiceMap.values()) {
if (fileService.getStorageTypeEnum() == type) {
result = fileService;
break;
}
}
return result;
}
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
}

View File

@@ -1,18 +0,0 @@
package im.zhaojun.common.config;
import im.zhaojun.common.model.enums.StorageTypeEnumDeSerializerConvert;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* @author zhaojun
*/
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new StorageTypeEnumDeSerializerConvert());
}
}

View File

@@ -1,23 +0,0 @@
package im.zhaojun.common.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.client.RestTemplate;
import java.nio.charset.StandardCharsets;
/**
* @author zhaojun
*/
@Configuration
public class ZFileConfiguration {
@Bean
public RestTemplate restTemplate(){
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));
return restTemplate;
}
}

View File

@@ -1,115 +0,0 @@
package im.zhaojun.common.controller;
import im.zhaojun.common.model.StorageConfig;
import im.zhaojun.common.model.dto.ResultBean;
import im.zhaojun.common.model.dto.SystemConfigDTO;
import im.zhaojun.common.model.enums.StorageTypeEnum;
import im.zhaojun.common.service.AbstractFileService;
import im.zhaojun.common.service.FileAsyncCacheService;
import im.zhaojun.common.service.StorageConfigService;
import im.zhaojun.common.service.SystemConfigService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;
/**
* 后台管理
* @author zhaojun
*/
@RestController
@RequestMapping("/admin")
public class AdminController {
private static final Logger log = LoggerFactory.getLogger(AdminController.class);
@Resource
private StorageConfigService storageConfigService;
@Resource
private SystemConfigService systemConfigService;
@Resource
private FileAsyncCacheService fileAsyncCacheService;
/**
* 获取系统配置
*/
@GetMapping("/config")
public ResultBean getConfig() {
SystemConfigDTO systemConfigDTO = systemConfigService.getSystemConfig();
return ResultBean.success(systemConfigDTO);
}
/**
* 更新系统配置
*/
@PostMapping("/update-pwd")
public ResultBean updatePwd(String username, String password) {
systemConfigService.updateUsernameAndPwd(username, password);
return ResultBean.success();
}
/**
* 更新系统配置
*/
@PostMapping("/config")
public ResultBean updateConfig(SystemConfigDTO systemConfigDTO) {
StorageTypeEnum currentStorageStrategy = systemConfigService.getCurrentStorageStrategy();
systemConfigDTO.setId(1);
systemConfigService.updateSystemConfig(systemConfigDTO);
if (!currentStorageStrategy.equals(systemConfigDTO.getStorageStrategy())) {
refreshStorageStrategy();
}
return ResultBean.success();
}
@GetMapping("/strategy-form")
public ResultBean getFormByStorageType(StorageTypeEnum storageType) {
List<StorageConfig> storageConfigList = storageConfigService.selectStorageConfigByType(storageType);
return ResultBean.success(storageConfigList);
}
/**
* 清理当前启用的存储引擎的缓存
*/
@GetMapping("/clear-cache")
public ResultBean clearCache() {
AbstractFileService fileService = systemConfigService.getCurrentFileService();
fileService.clearCache();
return ResultBean.success();
}
/**
* 更新存储策略
*/
public void refreshStorageStrategy() {
StorageTypeEnum storageStrategy = systemConfigService.getCurrentStorageStrategy();
refreshStorageStrategy(storageStrategy);
}
/**
* 更新存储策略
*/
public void refreshStorageStrategy(StorageTypeEnum storageStrategy) {
if (storageStrategy == null) {
log.info("尚未配置存储策略.");
} else {
AbstractFileService fileService = systemConfigService.getCurrentFileService();
fileService.init();
log.info("当前启用存储类型: {}", storageStrategy.getDescription());
// if 判断是否开启搜索.
fileAsyncCacheService.cacheGlobalFile();
}
}
}

View File

@@ -1,160 +0,0 @@
package im.zhaojun.common.controller;
import cn.hutool.core.util.URLUtil;
import im.zhaojun.common.annotation.CheckStorageStrategyInit;
import im.zhaojun.common.exception.SearchDisableException;
import im.zhaojun.common.model.StorageConfig;
import im.zhaojun.common.model.constant.ZFileConstant;
import im.zhaojun.common.model.dto.FileItemDTO;
import im.zhaojun.common.model.dto.ResultBean;
import im.zhaojun.common.model.dto.SiteConfigDTO;
import im.zhaojun.common.model.dto.SystemConfigDTO;
import im.zhaojun.common.model.enums.StorageTypeEnum;
import im.zhaojun.common.service.AbstractFileService;
import im.zhaojun.common.service.StorageConfigService;
import im.zhaojun.common.service.SystemConfigService;
import im.zhaojun.common.service.SystemService;
import im.zhaojun.common.util.AudioHelper;
import im.zhaojun.common.util.FileComparator;
import im.zhaojun.common.util.HttpUtil;
import im.zhaojun.common.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
/**
* 前台文件管理
* @author zhaojun
*/
@RequestMapping("/api")
@RestController
public class FileController {
@Resource
private SystemService systemService;
@Resource
private SystemConfigService systemConfigService;
@Resource
private StorageConfigService storageConfigService;
/**
* 滚动加载每页条数.
*/
private static final Integer PAGE_SIZE = 30;
@CheckStorageStrategyInit
@GetMapping("/list")
public ResultBean list(@RequestParam(defaultValue = "/") String path,
@RequestParam(defaultValue = "name") String sortBy,
@RequestParam(defaultValue = "asc") String order,
@RequestParam(required = false) String password,
@RequestParam(defaultValue = "1") Integer page) throws Exception {
AbstractFileService fileService = systemConfigService.getCurrentFileService();
List<FileItemDTO> fileItemList = fileService.fileList(StringUtils.removeDuplicateSeparator("/" + URLUtil.decode(path) + "/"));
for (FileItemDTO fileItemDTO : fileItemList) {
if (ZFileConstant.PASSWORD_FILE_NAME.equals(fileItemDTO.getName())
&& !HttpUtil.getTextContent(fileItemDTO.getUrl()).equals(password)) {
if (password != null && !"".equals(password)) {
return ResultBean.error("密码错误.");
}
return ResultBean.error("此文件夹需要密码.", ResultBean.REQUIRED_PASSWORD);
}
}
// 排序, 先按照文件类型比较, 文件夹在前, 文件在后, 然后根据 sortBy 字段排序, 默认为升序;
fileItemList.sort(new FileComparator(sortBy, order));
filterFileList(fileItemList);
int total = fileItemList.size();
int totalPage = (total + PAGE_SIZE - 1) / PAGE_SIZE;
if (page > totalPage) {
return ResultBean.successData(new ArrayList<>());
}
int start = (page - 1) * PAGE_SIZE;
int end = page * PAGE_SIZE;
end = Math.min(end, total);
List<FileItemDTO> fileSubItem = fileItemList.subList(start, end);
return ResultBean.successData(fileSubItem);
}
/**
* 获取文件类容, 仅限用于, txt, md, ini 等普通文本文件.
* @param url 文件路径
* @return 文件内容
*/
@CheckStorageStrategyInit
@GetMapping("/content")
public ResultBean getContent(String url) {
return ResultBean.successData(HttpUtil.getTextContent(url));
}
/**
* 获取系统配置信息和当前页的标题, 文件头, 文件尾信息
* @param path 路径
*/
@CheckStorageStrategyInit
@GetMapping("/config")
public ResultBean getConfig(String path) throws Exception {
SiteConfigDTO config = systemService.getConfig(URLUtil.decode(StringUtils.removeDuplicateSeparator("/" + path + "/")));
config.setSystemConfigDTO(systemConfigService.getSystemConfig());
return ResultBean.successData(config);
}
@CheckStorageStrategyInit
@GetMapping("/clearCache")
public ResultBean clearCache() {
AbstractFileService fileService = systemConfigService.getCurrentFileService();
if (fileService != null) {
fileService.clearCache();
}
return ResultBean.success();
}
@CheckStorageStrategyInit
@GetMapping("/audioInfo")
public ResultBean getAudioInfo(String url) throws Exception {
return ResultBean.success(AudioHelper.getAudioInfo(url));
}
@CheckStorageStrategyInit
@GetMapping("/search")
public ResultBean search(@RequestParam(value = "name", defaultValue = "/") String name) throws Exception {
AbstractFileService fileService = systemConfigService.getCurrentFileService();
SystemConfigDTO systemConfigDTO = systemConfigService.getSystemConfig();
if (!systemConfigDTO.getSearchEnable()) {
throw new SearchDisableException("搜索功能未开启");
}
return ResultBean.success(fileService.search(URLUtil.decode(name)));
}
@GetMapping("/form")
public ResultBean getFormByStorageType(String storageType) {
StorageTypeEnum storageTypeEnum = StorageTypeEnum.getEnum(storageType);
List<StorageConfig> storageConfigList = storageConfigService.selectStorageConfigByType(storageTypeEnum);
storageConfigList.forEach(storageConfig -> storageConfig.setValue(null));
return ResultBean.success(storageConfigList);
}
/**
* 过滤文件列表, 不显示密码, 头部和尾部文件.
*/
private void filterFileList(List<FileItemDTO> fileItemList) {
if (fileItemList == null) {
return;
}
fileItemList.removeIf(fileItem -> ZFileConstant.PASSWORD_FILE_NAME.equals(fileItem.getName())
|| ZFileConstant.FOOTER_FILE_NAME.equals(fileItem.getName())
|| ZFileConstant.HEADER_FILE_NAME.equals(fileItem.getName()));
}
}

View File

@@ -1,100 +0,0 @@
package im.zhaojun.common.controller;
import cn.hutool.crypto.SecureUtil;
import im.zhaojun.common.model.StorageConfig;
import im.zhaojun.common.model.dto.InstallModelDTO;
import im.zhaojun.common.model.dto.ResultBean;
import im.zhaojun.common.model.dto.SystemConfigDTO;
import im.zhaojun.common.model.enums.StorageTypeEnum;
import im.zhaojun.common.service.AbstractFileService;
import im.zhaojun.common.service.StorageConfigService;
import im.zhaojun.common.service.SystemConfigService;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* 系统安装初始化
* @author zhaojun
*/
@Controller
public class InstallController {
@Resource
private SystemConfigService systemConfigService;
@Resource
private StorageConfigService storageConfigService;
@Resource
private AdminController adminController;
@GetMapping("/is-installed")
@ResponseBody
public ResultBean isInstall() {
if (systemConfigService.getCurrentStorageStrategy() == null) {
return ResultBean.success();
}
return ResultBean.error("请勿重复初始化");
}
@PostMapping("/install")
@ResponseBody
public ResultBean install(InstallModelDTO installModelDTO) {
SystemConfigDTO systemConfigDTO = systemConfigService.getSystemConfig();
if (systemConfigDTO.getStorageStrategy() != null) {
return ResultBean.error("请勿重复初始化.");
}
systemConfigDTO.setSiteName(installModelDTO.getSiteName());
StorageTypeEnum storageTypeEnum = installModelDTO.getStorageStrategy();
systemConfigDTO.setStorageStrategy(storageTypeEnum);
systemConfigDTO.setUsername(installModelDTO.getUsername());
systemConfigDTO.setPassword(SecureUtil.md5(installModelDTO.getPassword()));
systemConfigDTO.setDomain(installModelDTO.getDomain());
systemConfigService.updateSystemConfig(systemConfigDTO);
Map<String, String> storageStrategyConfig = installModelDTO.getStorageStrategyConfig();
List<StorageConfig> storageConfigList = storageConfigService.selectStorageConfigByType(storageTypeEnum);
for (StorageConfig storageConfig : storageConfigList) {
String key = storageConfig.getKey();
String value = storageStrategyConfig.get(key);
storageConfig.setValue(value);
}
storageConfigService.updateStorageConfig(storageConfigList);
adminController.refreshStorageStrategy();
return ResultBean.success();
}
@PostMapping("/storage-strategy")
@ResponseBody
public ResultBean save(@RequestParam Map<String, String> storageStrategyConfig, StorageTypeEnum storageStrategy) {
List<StorageConfig> storageConfigList = storageConfigService.selectStorageConfigByType(storageStrategy);
for (StorageConfig storageConfig : storageConfigList) {
String key = storageConfig.getKey();
String value = storageStrategyConfig.get(key);
storageConfig.setValue(value);
}
storageConfigService.updateStorageConfig(storageConfigList);
StorageTypeEnum currentStorageStrategy = systemConfigService.getCurrentStorageStrategy();
if (Objects.equals(storageStrategy, currentStorageStrategy)) {
AbstractFileService fileService = systemConfigService.getCurrentFileService();
fileService.clearCache();
fileService.init();
}
return ResultBean.success();
}
}

View File

@@ -1,64 +0,0 @@
package im.zhaojun.common.exception;
import im.zhaojun.common.model.dto.ResultBean;
import org.apache.catalina.connector.ClientAbortException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
/**
* 全局异常处理器
* @author zhaojun
*/
@ControllerAdvice
@ResponseBody
public class GlobleExceptionHandler {
private static final Logger log = LoggerFactory.getLogger(GlobleExceptionHandler.class);
@ExceptionHandler(SearchDisableException.class)
@ResponseStatus(code= HttpStatus.INTERNAL_SERVER_ERROR)
public ResultBean searchDisableExceptionHandler(SearchDisableException e) {
if (log.isDebugEnabled()) {
log.debug(e.getMessage(), e);
}
return ResultBean.error(e.getMessage());
}
@ExceptionHandler
@ResponseStatus
public ResultBean searchDisableExceptionHandler(StorageStrategyUninitializedException e) {
if (log.isDebugEnabled()) {
log.debug(e.getMessage(), e);
}
return ResultBean.error(e.getMessage());
}
/**
* 捕获 ClientAbortException 异常, 不做任何处理, 防止出现大量堆栈日志输出, 此异常不影响功能.
*/
@ExceptionHandler({HttpMediaTypeNotAcceptableException.class, ClientAbortException.class})
@ResponseBody
@ResponseStatus
public void clientAbortException(Exception ex) {
// if (log.isDebugEnabled()) {
// log.debug("出现了断开异常:", ex);
// }
}
@ExceptionHandler
@ResponseStatus(code= HttpStatus.INTERNAL_SERVER_ERROR)
public ResultBean searchDisableExceptionHandler(Exception e) {
if (log.isDebugEnabled()) {
log.debug(e.getMessage(), e);
}
return ResultBean.error("系统异常, 请联系管理员");
}
}

View File

@@ -1,29 +0,0 @@
package im.zhaojun.common.exception;
/**
* 对象存储初始化异常
* @author zhaojun
*/
public class InitializeException extends RuntimeException {
private static final long serialVersionUID = -1920550904063819880L;
public InitializeException() {
}
public InitializeException(String message) {
super(message);
}
public InitializeException(String message, Throwable cause) {
super(message, cause);
}
public InitializeException(Throwable cause) {
super(cause);
}
public InitializeException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}

View File

@@ -1,26 +0,0 @@
package im.zhaojun.common.exception;
/**
* @author zhaojun
*/
public class SearchDisableException extends RuntimeException {
public SearchDisableException() {
}
public SearchDisableException(String message) {
super(message);
}
public SearchDisableException(String message, Throwable cause) {
super(message, cause);
}
public SearchDisableException(Throwable cause) {
super(cause);
}
public SearchDisableException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}

View File

@@ -1,29 +0,0 @@
package im.zhaojun.common.exception;
/**
* 存储策略未初始化异常
* @author zhaojun
*/
public class StorageStrategyUninitializedException extends RuntimeException {
private static final long serialVersionUID = 5736940575583615661L;
public StorageStrategyUninitializedException() {
}
public StorageStrategyUninitializedException(String message) {
super(message);
}
public StorageStrategyUninitializedException(String message, Throwable cause) {
super(message, cause);
}
public StorageStrategyUninitializedException(Throwable cause) {
super(cause);
}
public StorageStrategyUninitializedException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}

View File

@@ -1,29 +0,0 @@
package im.zhaojun.common.exception;
/**
* 未知的存储类型异常
* @author zhaojun
*/
public class UnknownStorageTypeException extends RuntimeException {
private static final long serialVersionUID = -4853756482605773655L;
public UnknownStorageTypeException() {
}
public UnknownStorageTypeException(String message) {
super(message);
}
public UnknownStorageTypeException(String message, Throwable cause) {
super(message, cause);
}
public UnknownStorageTypeException(Throwable cause) {
super(cause);
}
public UnknownStorageTypeException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}

View File

@@ -1,71 +0,0 @@
package im.zhaojun.common.model;
import im.zhaojun.common.model.enums.StorageTypeEnum;
import lombok.Data;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
/**
* @author zhaojun
*/
@Entity(name = "STORAGE_CONFIG")
@Data
public class StorageConfig {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private StorageTypeEnum type;
@Column(name = "k")
private String key;
private String title;
private String value;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public StorageTypeEnum getType() {
return type;
}
public void setType(StorageTypeEnum type) {
this.type = type;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}

View File

@@ -1,60 +0,0 @@
package im.zhaojun.common.model;
import lombok.Data;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
/**
* @author zhaojun
*/
@Entity(name = "SYSTEM_CONFIG")
@Data
public class SystemConfig {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(name = "k")
private String key;
private String value;
private String remark;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
}

View File

@@ -1,33 +0,0 @@
package im.zhaojun.common.model.constant;
/**
* @author zhaojun
* @date 2019/12/28 18:47
*/
public class StorageConfigConstant {
public static final String BUCKET_NAME_KEY = "bucket-name";
public static final String SECRET_ID_KEY = "secretId";
public static final String ACCESS_KEY = "accessKey";
public static final String SECRET_KEY = "secretKey";
public static final String ENDPOINT_KEY = "endPoint";
public static final String BASE_PATH = "base-path";
public static final String DOMAIN_KEY = "domain";
public static final String USERNAME_KEY = "username";
public static final String PASSWORD_KEY = "password";
public static final String HOST_KEY = "host";
public static final String PORT_KEY = "port";
public static final String FILE_PATH_KEY = "filePath";
}

View File

@@ -1,26 +0,0 @@
package im.zhaojun.common.model.constant;
/**
* @author zhaojun
*/
public class SystemConfigConstant {
public static final String SITE_NAME = "siteName";
public static final String INFO_ENABLE = "infoEnable";
public static final String SEARCH_ENABLE = "searchEnable";
public static final String SEARCH_IGNORE_CASE = "searchIgnoreCase";
public static final String ENABLE_CACHE = "enableCache";
public static final String STORAGE_STRATEGY = "storageStrategy";
public static final String USERNAME = "username";
public static final String PASSWORD = "password";
public static final String DOMAIN = "domain";
}

View File

@@ -1,47 +0,0 @@
package im.zhaojun.common.model.constant;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
/**
* @author zhaojun
*/
@Configuration
public class ZFileConstant {
public final static String USER_HOME = System.getProperty("user.home");
public static final String AUDIO_TMP_PATH = "/.zfile/tmp/audio/";
/**
* 页面头部文件
*/
public static String HEADER_FILE_NAME = "header.md";
/**
* 页面尾部文件
*/
public static String FOOTER_FILE_NAME = "footer.md";
/**
* 密码文件
*/
public static String PASSWORD_FILE_NAME = "password.txt";
@Autowired(required = false)
public void setHeaderFileName(@Value("${zfile.constant.header}") String headerFileName) {
ZFileConstant.HEADER_FILE_NAME = headerFileName;
}
@Autowired(required = false)
public void setFooterFileName(@Value("${zfile.constant.footer}") String footerFileName) {
ZFileConstant.FOOTER_FILE_NAME = footerFileName;
}
@Autowired(required = false)
public void setPasswordFileName(@Value("${zfile.constant.password}") String passwordFileName) {
ZFileConstant.PASSWORD_FILE_NAME = passwordFileName;
}
}

View File

@@ -1,53 +0,0 @@
package im.zhaojun.common.model.dto;
/**
* @author zhaojun
*/
public class AudioInfoDTO {
private String title;
private String artist;
private String cover;
private String src;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getArtist() {
return artist;
}
public void setArtist(String artist) {
this.artist = artist;
}
public String getCover() {
return cover;
}
public void setCover(String cover) {
this.cover = cover;
}
public String getSrc() {
return src;
}
public void setSrc(String src) {
this.src = src;
}
@Override
public String toString() {
return "AudioInfoDTO{" +
"title='" + title + '\'' +
", artist='" + artist + '\'' +
", cover='" + cover + '\'' +
", src='" + src + '\'' +
'}';
}
}

View File

@@ -1,79 +0,0 @@
package im.zhaojun.common.model.dto;
import im.zhaojun.common.model.enums.FileTypeEnum;
import java.io.Serializable;
import java.util.Date;
/**
* @author zhaojun
*/
public class FileItemDTO implements Serializable {
private String name;
private Date time;
private Long size;
private FileTypeEnum type;
private String path;
private String url;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getTime() {
return time;
}
public void setTime(Date time) {
this.time = time;
}
public Long getSize() {
return size;
}
public void setSize(Long size) {
this.size = size;
}
public FileTypeEnum getType() {
return type;
}
public void setType(FileTypeEnum type) {
this.type = type;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
@Override
public String toString() {
return "FileItemDTO{" +
"name='" + name + '\'' +
", time=" + time +
", size=" + size +
", type=" + type +
", path='" + path + '\'' +
", url='" + url + '\'' +
'}';
}
}

View File

@@ -1,77 +0,0 @@
package im.zhaojun.common.model.dto;
import im.zhaojun.common.model.enums.StorageTypeEnum;
import java.util.Map;
/**
* @author zhaojun
*/
public class InstallModelDTO {
private String siteName;
private StorageTypeEnum storageStrategy;
private String username;
private String password;
private String domain;
private Map<String, String> storageStrategyConfig;
public String getSiteName() {
return siteName;
}
public void setSiteName(String siteName) {
this.siteName = siteName;
}
public StorageTypeEnum getStorageStrategy() {
return storageStrategy;
}
public void setStorageStrategy(StorageTypeEnum storageStrategy) {
this.storageStrategy = storageStrategy;
}
public Map<String, String> getStorageStrategyConfig() {
return storageStrategyConfig;
}
public void setStorageStrategyConfig(Map<String, String> storageStrategyConfig) {
this.storageStrategyConfig = storageStrategyConfig;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getDomain() {
return domain;
}
public void setDomain(String domain) {
this.domain = domain;
}
@Override
public String toString() {
return "InstallModelDTO{" +
"siteName='" + siteName + '\'' +
", storageStrategy=" + storageStrategy +
", username='" + username + '\'' +
", password='" + password + '\'' +
", domain='" + domain + '\'' +
", storageStrategyConfig=" + storageStrategyConfig +
'}';
}
}

View File

@@ -1,91 +0,0 @@
package im.zhaojun.common.model.dto;
import java.io.Serializable;
/**
* @author zhaojun
*/
public class ResultBean implements Serializable {
private static final long serialVersionUID = -8276264968757808344L;
public static final int SUCCESS = 0;
public static final int FAIL = -1;
public static final int REQUIRED_PASSWORD = -2;
private String msg = "操作成功";
private int code = SUCCESS;
private Object data;
private ResultBean() {
super();
}
private ResultBean(String msg, Object data, int code) {
this.msg = msg;
this.data = data;
this.code = code;
}
public static ResultBean success() {
return success("操作成功");
}
public static ResultBean success(String msg) {
return success(msg, null);
}
public static ResultBean successData(Object data) {
return success("操作成功", data);
}
public static ResultBean success(Object data) {
return success("操作成功", data);
}
public static ResultBean success(String msg, Object data) {
return new ResultBean(msg, data, SUCCESS);
}
public static ResultBean error(String msg) {
ResultBean resultBean = new ResultBean();
resultBean.setCode(FAIL);
resultBean.setMsg(msg);
return resultBean;
}
public static ResultBean error(String msg, Integer code) {
ResultBean resultBean = new ResultBean();
resultBean.setCode(code);
resultBean.setMsg(msg);
return resultBean;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
}

View File

@@ -1,53 +0,0 @@
package im.zhaojun.common.model.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serializable;
/**
* @author zhaojun
*/
public class SiteConfigDTO implements Serializable {
private static final long serialVersionUID = 8811196207046121740L;
private String header;
private String footer;
@JsonProperty("viewConfig")
private SystemConfigDTO systemConfigDTO;
public String getHeader() {
return header;
}
public void setHeader(String header) {
this.header = header;
}
public String getFooter() {
return footer;
}
public void setFooter(String footer) {
this.footer = footer;
}
public SystemConfigDTO getSystemConfigDTO() {
return systemConfigDTO;
}
public void setSystemConfigDTO(SystemConfigDTO systemConfigDTO) {
this.systemConfigDTO = systemConfigDTO;
}
@Override
public String toString() {
return "SiteConfigDTO{" +
"header='" + header + '\'' +
", footer='" + footer + '\'' +
", systemConfig=" + systemConfigDTO +
'}';
}
}

View File

@@ -1,40 +0,0 @@
package im.zhaojun.common.model.dto;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import im.zhaojun.common.model.enums.StorageTypeEnum;
import im.zhaojun.common.model.enums.StorageTypeEnumSerializerConvert;
import lombok.Data;
import lombok.ToString;
/**
* 系统设置传输类
* @author zhaojun
*/
@ToString
@Data
public class SystemConfigDTO {
@JsonIgnore
private Integer id;
private String siteName;
private Boolean infoEnable;
private Boolean searchEnable;
private Boolean searchIgnoreCase;
@JsonSerialize(using = StorageTypeEnumSerializerConvert.class)
private StorageTypeEnum storageStrategy;
private String username;
@JsonIgnore
private String password;
private String domain;
private Boolean enableCache;
}

View File

@@ -1,31 +0,0 @@
package im.zhaojun.common.model.enums;
/**
* @author zhaojun
*/
public enum FileTypeEnum {
/**
* 文件
*/
FILE("File"),
/**
* 文件夹
*/
FOLDER("Folder");
private String value;
FileTypeEnum(String value) {
this.value = value;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}

View File

@@ -1,59 +0,0 @@
package im.zhaojun.common.model.enums;
import java.util.HashMap;
import java.util.Map;
/**
* @author zhaojun
*/
public enum StorageTypeEnum {
/**
* 当前系统支持的所有存储策略
*/
UPYUN("upyun", "又拍云 USS"),
QINIU("qiniu", "七牛云 KODO"),
HUAWEI("huawei", "华为云 OBS"),
ALIYUN("aliyun", "阿里云 OSS"),
FTP("ftp", "FTP"),
LOCAL("local", "本地存储"),
TENCENT("tencent", "腾讯云 COS"),
MINIO("minio", "MINIO");
private String key;
private String description;
private static Map<String, StorageTypeEnum> enumMap = new HashMap<>();
static {
for (StorageTypeEnum type : StorageTypeEnum.values()) {
enumMap.put(type.getKey(), type);
}
}
StorageTypeEnum(String key, String description) {
this.key = key;
this.description = description;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public static StorageTypeEnum getEnum(String value) {
return enumMap.get(value);
}
}

View File

@@ -1,22 +0,0 @@
package im.zhaojun.common.model.enums;
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
/**
* @author zhaojun
*/
@Converter(autoApply = true)
public class StorageTypeEnumConvert implements AttributeConverter<StorageTypeEnum, String> {
@Override
public String convertToDatabaseColumn(StorageTypeEnum attribute) {
return attribute.getKey();
}
@Override
public StorageTypeEnum convertToEntityAttribute(String dbData) {
return StorageTypeEnum.getEnum(dbData);
}
}

View File

@@ -1,15 +0,0 @@
package im.zhaojun.common.model.enums;
import org.springframework.core.convert.converter.Converter;
import org.springframework.lang.NonNull;
/**
* @author zhaojun
*/
public class StorageTypeEnumDeSerializerConvert implements Converter<String, StorageTypeEnum> {
@Override
public StorageTypeEnum convert(@NonNull String s) {
return StorageTypeEnum.getEnum(s);
}
}

View File

@@ -1,18 +0,0 @@
package im.zhaojun.common.model.enums;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
/**
* @author zhaojun
*/
public class StorageTypeEnumSerializerConvert extends JsonSerializer<StorageTypeEnum> {
@Override
public void serialize(StorageTypeEnum storageTypeEnum, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeString(storageTypeEnum.getKey());
}
}

View File

@@ -1,23 +0,0 @@
package im.zhaojun.common.repository;
import im.zhaojun.common.model.StorageConfig;
import im.zhaojun.common.model.enums.StorageTypeEnum;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* @author zhaojun
*/
@Repository
public interface StorageConfigRepository extends JpaRepository<StorageConfig, Integer> {
/**
* 根据存储类型找对应的配置信息
* @param type 存储类型
* @return 此类型所有的配置信息
*/
List<StorageConfig> findByTypeOrderById(StorageTypeEnum type);
}

View File

@@ -1,19 +0,0 @@
package im.zhaojun.common.repository;
import im.zhaojun.common.model.SystemConfig;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
/**
* @author zhaojun
*/
@Repository
public interface SystemConfigRepository extends JpaRepository<SystemConfig, Integer> {
/**
* 查找系统设置中, 某个设置项对应的值
* @param key 设置项
* @return 设置值
*/
SystemConfig findByKey(String key);
}

View File

@@ -1,20 +0,0 @@
package im.zhaojun.common.security;
import cn.hutool.crypto.SecureUtil;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
* @author zhaojun
*/
public class Md5PasswordEncoder implements PasswordEncoder {
@Override
public String encode(CharSequence rawPassword) {
return SecureUtil.md5(rawPassword.toString());
}
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
return SecureUtil.md5(rawPassword.toString()).equals(encodedPassword);
}
}

View File

@@ -1,26 +0,0 @@
package im.zhaojun.common.security;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
/**
* @author zhaojun
*/
@Configuration
public class MyCorsFilter {
@Bean
public CorsFilter corsFilter() {
final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
final CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.setAllowCredentials(true);
corsConfiguration.addAllowedOrigin("*");
corsConfiguration.addAllowedHeader("*");
corsConfiguration.addAllowedMethod("*");
urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration);
return new CorsFilter(urlBasedCorsConfigurationSource);
}
}

View File

@@ -1,122 +0,0 @@
package im.zhaojun.common.security;
import com.fasterxml.jackson.databind.ObjectMapper;
import im.zhaojun.common.model.dto.ResultBean;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
/**
* 自定义 Security 配置类
* @author zhaojun
*/
@EnableWebSecurity
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
@Resource
private ObjectMapper objectMapper;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
// .authenticationProvider(authenticationProvider())
.exceptionHandling()
//未登录时进行json格式的提示很喜欢这种写法不用单独写一个又一个的类
.authenticationEntryPoint((request, response, authException) -> {
response.setContentType("application/json;charset=utf-8");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
PrintWriter out = response.getWriter();
out.write(objectMapper.writeValueAsString(ResultBean.error("未登录")));
out.flush();
out.close();
})
.and()
.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/admin/**").authenticated()
.and()
.formLogin() //使用自带的登录
//登录失败返回json
.failureHandler((request, response, ex) -> {
response.setContentType("application/json;charset=utf-8");
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
PrintWriter out = response.getWriter();
String msg;
if (ex instanceof UsernameNotFoundException || ex instanceof BadCredentialsException) {
msg = "用户名或密码错误";
} else {
msg = "登录失败";
}
out.write(objectMapper.writeValueAsString(ResultBean.error(msg)));
out.flush();
out.close();
})
//登录成功返回json
.successHandler((request, response, authentication) -> {
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
out.write(objectMapper.writeValueAsString(ResultBean.success(authentication)));
out.flush();
out.close();
})
.and()
.exceptionHandling()
//没有权限返回json
.accessDeniedHandler((request, response, ex) -> {
response.setContentType("application/json;charset=utf-8");
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
PrintWriter out = response.getWriter();
out.write(objectMapper.writeValueAsString(ResultBean.error("权限不足")));
out.flush();
out.close();
})
.and()
.logout()
//退出成功返回json
.logoutSuccessHandler((request, response, authentication) -> {
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
out.write(objectMapper.writeValueAsString(ResultBean.error("注销成功")));
out.flush();
out.close();
})
.and()
.logout().permitAll();
http.cors();
http.csrf().disable();
}
@Override
public void configure(AuthenticationManagerBuilder web) throws Exception {
web.userDetailsService(myUserDetailsServiceImpl()).passwordEncoder(passwordEncoder());
}
@Bean
public MyUserDetailsServiceImpl myUserDetailsServiceImpl() {
return new MyUserDetailsServiceImpl();
}
@Override
public void configure(WebSecurity web) {
//对于在header里面增加token等类似情况放行所有OPTIONS请求。
web.ignoring().antMatchers(HttpMethod.OPTIONS, "/**");
}
@Bean
public static PasswordEncoder passwordEncoder() {
return new Md5PasswordEncoder();
}
}

View File

@@ -1,29 +0,0 @@
package im.zhaojun.common.security;
import im.zhaojun.common.model.dto.SystemConfigDTO;
import im.zhaojun.common.service.SystemConfigService;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import javax.annotation.Resource;
import java.util.Collections;
/**
* @author zhaojun
*/
public class MyUserDetailsServiceImpl implements UserDetailsService {
@Resource
private SystemConfigService systemConfigService;
/**
* 授权的时候是对角色授权,认证的时候应该基于资源,而不是角色,因为资源是不变的,而用户的角色是会变的
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
SystemConfigDTO systemConfig = systemConfigService.getSystemConfig();
return new User(systemConfig.getUsername(), systemConfig.getPassword(), Collections.emptyList());
}
}

View File

@@ -1,132 +0,0 @@
package im.zhaojun.common.service;
import com.alicp.jetcache.Cache;
import com.alicp.jetcache.anno.CacheRefresh;
import com.alicp.jetcache.anno.CacheType;
import com.alicp.jetcache.anno.Cached;
import com.alicp.jetcache.anno.CreateCache;
import im.zhaojun.common.model.dto.FileItemDTO;
import im.zhaojun.common.model.enums.FileTypeEnum;
import im.zhaojun.common.model.enums.StorageTypeEnum;
import im.zhaojun.common.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Value;
import javax.annotation.PostConstruct;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* @author zhaojun
* @date 2019/12/28 19:27
*/
@Slf4j
public abstract class AbstractFileService implements FileService {
@Value("${zfile.cache.timeout}")
protected Long timeout;
protected boolean isInitialized;
@CreateCache(name = "zfile-cache:")
private Cache<String, Object> userCache;
/***
* 获取指定路径下的文件及文件夹, 默认缓存 60 分钟,每隔 30 分钟刷新一次.
* @param path 文件路径
* @return 文件及文件夹列表
* @throws Exception 获取文件列表中出现的异常
*/
@Override
@Cached(name = "zfile-cache:",
key = "args[0]",
cacheType = CacheType.LOCAL, condition = "mvel{bean('systemConfigService').enableCache}")
@CacheRefresh(refresh = 30, timeUnit = TimeUnit.MINUTES)
public abstract List<FileItemDTO> fileList(String path) throws Exception;
/**
* 清理当前存储引擎的缓存
*/
public void clearCache() {
}
/**
* 初始化方法, 启动时自动调用实现类的此方法进行初始化.
*/
@PostConstruct
public abstract void init();
protected boolean testConnection() {
boolean flag = true;
try {
fileList("/");
} catch (Exception e) {
e.printStackTrace();
flag = false;
}
return flag;
}
/**
* 获取是否初始化成功
* @return 初始化成功与否
*/
public boolean getIsInitialized() {
return isInitialized;
}
/**
* 获取存储引擎类型
* @return 存储引擎类型枚举
*/
public abstract StorageTypeEnum getStorageTypeEnum();
/**
* 搜索文件
* @param name 文件名
* @return 包含该文件名的所有文件或文件夹
* @throws Exception 搜索过程出现的异常
*/
public List<FileItemDTO> search(String name) throws Exception {
List<FileItemDTO> result = new ArrayList<>();
List<FileItemDTO> fileItemList = selectAllFileList();
for (FileItemDTO fileItemDTO : fileItemList) {
if (fileItemDTO.getName().contains(name)) {
result.add(fileItemDTO);
}
}
return result;
}
/**
* 查询所有文件
* @return 所有文件
* @throws Exception 异常现象
*/
public List<FileItemDTO> selectAllFileList() throws Exception {
List<FileItemDTO> result = new ArrayList<>();
String path = "/";
FileService currentFileService = (FileService) AopContext.currentProxy();
List<FileItemDTO> fileItemList = currentFileService.fileList(path);
ArrayDeque<FileItemDTO> queue = new ArrayDeque<>(fileItemList);
while (!queue.isEmpty()) {
FileItemDTO fileItemDTO = queue.pop();
result.add(fileItemDTO);
if (fileItemDTO.getType() == FileTypeEnum.FOLDER) {
String filePath = StringUtils.removeDuplicateSeparator("/" + fileItemDTO.getPath() + "/" + fileItemDTO.getName() + "/");
queue.addAll(currentFileService.fileList(filePath));
}
}
return result;
}
}

View File

@@ -1,111 +0,0 @@
package im.zhaojun.common.service;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.URLUtil;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.model.ListObjectsRequest;
import com.amazonaws.services.s3.model.ObjectListing;
import com.amazonaws.services.s3.model.S3ObjectSummary;
import im.zhaojun.common.model.dto.FileItemDTO;
import im.zhaojun.common.model.enums.FileTypeEnum;
import im.zhaojun.common.util.StringUtils;
import javax.annotation.Resource;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* @author zhaojun
* @date 2019/12/26 22:26
*/
public abstract class AbstractS3FileService extends AbstractFileService {
@Resource
protected StorageConfigService storageConfigService;
protected String path;
protected String basePath;
protected String bucketName;
protected String domain;
protected AmazonS3 s3Client;
@Override
public List<FileItemDTO> fileList(String path) {
this.path = path;
return s3FileList(path);
}
@Override
public String getDownloadUrl(String path) {
this.path = path;
return s3ObjectUrl(path);
}
/**
* 获取 S3 指定目录下的对象列表
* @param path 路径
* @return 指定目录下的对象列表
*/
public List<FileItemDTO> s3FileList(String path) {
path = StringUtils.removeFirstSeparator(path);
String fullPath = StringUtils.removeFirstSeparator(getFullPath());
List<FileItemDTO> fileItemList = new ArrayList<>();
ObjectListing objectListing = s3Client.listObjects(new ListObjectsRequest(bucketName, fullPath, "", "/", 1000));
for (S3ObjectSummary s : objectListing.getObjectSummaries()) {
FileItemDTO fileItemDTO = new FileItemDTO();
if (s.getKey().equals(fullPath)) {
continue;
}
fileItemDTO.setName(s.getKey().substring(fullPath.length()));
fileItemDTO.setSize(s.getSize());
fileItemDTO.setTime(s.getLastModified());
fileItemDTO.setType(FileTypeEnum.FILE);
fileItemDTO.setPath(path);
fileItemDTO.setUrl(getDownloadUrl(StringUtils.concatUrl(path, fileItemDTO.getName())));
fileItemList.add(fileItemDTO);
}
for (String commonPrefix : objectListing.getCommonPrefixes()) {
FileItemDTO fileItemDTO = new FileItemDTO();
fileItemDTO.setName(commonPrefix.substring(fullPath.length(), commonPrefix.length() - 1));
fileItemDTO.setType(FileTypeEnum.FOLDER);
fileItemDTO.setPath(path);
fileItemList.add(fileItemDTO);
}
return fileItemList;
}
/**
* 获取对象的访问链接, 如果指定了域名, 则替换为自定义域名.
* @return S3 对象访问地址
*/
public String s3ObjectUrl(String path) {
String fullPath = StringUtils.removeFirstSeparator(StringUtils.removeDuplicateSeparator(basePath + "/" + path));
Date expirationDate = new Date(System.currentTimeMillis() + timeout * 1000);
URL url = s3Client.generatePresignedUrl(bucketName, fullPath, expirationDate);
String defaultUrl = url.toExternalForm();
if (StringUtils.isNotNullOrEmpty(domain)) {
defaultUrl = URLUtil.complateUrl(domain, url.getFile());
}
return URLUtil.decode(defaultUrl);
}
/**
* 获取 basePath + path 的全路径地址.
* @return basePath + path 的全路径地址.
*/
public String getFullPath() {
String basePath = ObjectUtil.defaultIfNull(this.basePath, "");
String path = ObjectUtil.defaultIfNull(this.path, "");
return StringUtils.removeDuplicateSeparator(basePath + "/" + path);
}
}

View File

@@ -1,46 +0,0 @@
package im.zhaojun.common.service;
import im.zhaojun.common.config.StorageTypeFactory;
import im.zhaojun.common.model.enums.StorageTypeEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
/**
* @author zhaojun
*/
@Service
public class FileAsyncCacheService {
private static final Logger log = LoggerFactory.getLogger(FileAsyncCacheService.class);
@Resource
private SystemConfigService systemConfigService;
@Async
public void cacheGlobalFile() {
StorageTypeEnum storageStrategy = systemConfigService.getCurrentStorageStrategy();
if (storageStrategy == null) {
log.info("尚未配置存储策略. 跳过启动缓存.");
return;
}
AbstractFileService fileService = StorageTypeFactory.getStorageTypeService(storageStrategy);
log.info("缓存 {} 所有文件开始", storageStrategy.getDescription());
long startTime = System.currentTimeMillis();
try {
if (fileService.getIsInitialized()) {
fileService.selectAllFileList();
}
} catch (Exception e) {
log.error("缓存所有文件失败", e);
e.printStackTrace();
}
long endTime = System.currentTimeMillis();
log.info("缓存 {} 所有文件结束, 用时: {} 秒", storageStrategy.getDescription(), ( (endTime - startTime) / 1000 ));
}
}

View File

@@ -1,27 +0,0 @@
package im.zhaojun.common.service;
import im.zhaojun.common.model.dto.FileItemDTO;
import java.util.List;
/**
* @author zhaojun
*/
public interface FileService {
/***
* 获取指定路径下的文件及文件夹, 默认缓存 60 分钟,每隔 30 分钟刷新一次.
* @param path 文件路径
* @return 文件及文件夹列表
* @throws Exception 获取文件列表中出现的异常
*/
List<FileItemDTO> fileList(String path) throws Exception;
/**
* 获取文件下载地址
* @param path 文件路径
* @return 文件下载地址
*/
String getDownloadUrl(String path);
}

View File

@@ -1,38 +0,0 @@
package im.zhaojun.common.service;
import im.zhaojun.common.model.StorageConfig;
import im.zhaojun.common.model.enums.StorageTypeEnum;
import im.zhaojun.common.repository.StorageConfigRepository;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author zhaojun
*/
@Service
public class StorageConfigService {
@Resource
private StorageConfigRepository storageConfigRepository;
public List<StorageConfig> selectStorageConfigByType(StorageTypeEnum storageTypeEnum) {
return storageConfigRepository.findByTypeOrderById(storageTypeEnum);
}
public Map<String, StorageConfig> selectStorageConfigMapByKey(StorageTypeEnum storageTypeEnum) {
Map<String, StorageConfig> map = new HashMap<>(24);
for (StorageConfig storageConfig : selectStorageConfigByType(storageTypeEnum)) {
map.put(storageConfig.getKey(), storageConfig);
}
return map;
}
public void updateStorageConfig(List<StorageConfig> storageConfigList) {
storageConfigRepository.saveAll(storageConfigList);
}
}

View File

@@ -1,140 +0,0 @@
package im.zhaojun.common.service;
import cn.hutool.crypto.SecureUtil;
import im.zhaojun.common.config.StorageTypeFactory;
import im.zhaojun.common.model.SystemConfig;
import im.zhaojun.common.model.constant.SystemConfigConstant;
import im.zhaojun.common.model.dto.SystemConfigDTO;
import im.zhaojun.common.model.enums.StorageTypeEnum;
import im.zhaojun.common.repository.SystemConfigRepository;
import im.zhaojun.common.util.StringUtils;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
/**
* @author zhaojun
*/
@Service
public class SystemConfigService {
@Resource
private SystemConfigRepository systemConfigRepository;
public SystemConfigDTO getSystemConfig() {
SystemConfigDTO systemConfigDTO = new SystemConfigDTO();
List<SystemConfig> systemConfigList = systemConfigRepository.findAll();
for (SystemConfig systemConfig : systemConfigList) {
switch (systemConfig.getKey()) {
case SystemConfigConstant.SITE_NAME:
systemConfigDTO.setSiteName(systemConfig.getValue());
break;
case SystemConfigConstant.INFO_ENABLE:
systemConfigDTO.setInfoEnable("true".equals(systemConfig.getValue()));
break;
case SystemConfigConstant.SEARCH_ENABLE:
systemConfigDTO.setSearchEnable("true".equals(systemConfig.getValue()));
break;
case SystemConfigConstant.SEARCH_IGNORE_CASE:
systemConfigDTO.setSearchIgnoreCase("true".equals(systemConfig.getValue()));
break;
case SystemConfigConstant.STORAGE_STRATEGY:
String value = systemConfig.getValue();
systemConfigDTO.setStorageStrategy(StorageTypeEnum.getEnum(value));
break;
case SystemConfigConstant.USERNAME:
systemConfigDTO.setUsername(systemConfig.getValue());
break;
case SystemConfigConstant.PASSWORD:
systemConfigDTO.setPassword(systemConfig.getValue());
break;
case SystemConfigConstant.DOMAIN:
systemConfigDTO.setDomain(systemConfig.getValue());
break;
case SystemConfigConstant.ENABLE_CACHE:
systemConfigDTO.setEnableCache("true".equals(systemConfig.getValue()));
break;
default:break;
}
}
return systemConfigDTO;
}
public void updateSystemConfig(SystemConfigDTO systemConfigDTO) {
List<SystemConfig> systemConfigList = new ArrayList<>();
SystemConfig systemConfig = systemConfigRepository.findByKey(SystemConfigConstant.SITE_NAME);
systemConfig.setValue(systemConfigDTO.getSiteName());
systemConfigList.add(systemConfig);
SystemConfig domainConfig = systemConfigRepository.findByKey(SystemConfigConstant.DOMAIN);
domainConfig.setValue(systemConfigDTO.getDomain());
systemConfigList.add(domainConfig);
SystemConfig infoEnableSystemConfig = systemConfigRepository.findByKey(SystemConfigConstant.INFO_ENABLE);
infoEnableSystemConfig.setValue(systemConfigDTO.getInfoEnable() ? "true" : "false");
systemConfigList.add(infoEnableSystemConfig);
SystemConfig searchEnableSystemConfig = systemConfigRepository.findByKey(SystemConfigConstant.SEARCH_ENABLE);
searchEnableSystemConfig.setValue(systemConfigDTO.getSearchEnable() ? "true" : "false");
systemConfigList.add(searchEnableSystemConfig);
SystemConfig searchIgnoreCaseSystemConfig = systemConfigRepository.findByKey(SystemConfigConstant.SEARCH_IGNORE_CASE);
searchIgnoreCaseSystemConfig.setValue(systemConfigDTO.getSearchIgnoreCase() ? "true" : "false");
systemConfigList.add(searchIgnoreCaseSystemConfig);
SystemConfig enableCacheSystemConfig = systemConfigRepository.findByKey(SystemConfigConstant.ENABLE_CACHE);
enableCacheSystemConfig.setValue(systemConfigDTO.getEnableCache() ? "true" : "false");
systemConfigList.add(enableCacheSystemConfig);
SystemConfig storageStrategySystemConfig = systemConfigRepository.findByKey(SystemConfigConstant.STORAGE_STRATEGY);
storageStrategySystemConfig.setValue(systemConfigDTO.getStorageStrategy().getKey());
systemConfigList.add(storageStrategySystemConfig);
if (StringUtils.isNotNullOrEmpty(systemConfigDTO.getUsername())) {
SystemConfig usernameSystemConfig = systemConfigRepository.findByKey(SystemConfigConstant.USERNAME);
usernameSystemConfig.setValue(systemConfigDTO.getUsername());
systemConfigList.add(usernameSystemConfig);
}
if (StringUtils.isNotNullOrEmpty(systemConfigDTO.getPassword())) {
SystemConfig passwordSystemConfig = systemConfigRepository.findByKey(SystemConfigConstant.PASSWORD);
passwordSystemConfig.setValue(systemConfigDTO.getPassword());
systemConfigList.add(passwordSystemConfig);
}
systemConfigRepository.saveAll(systemConfigList);
}
public void updateUsernameAndPwd(String username, String password) {
SystemConfig usernameConfig = systemConfigRepository.findByKey(SystemConfigConstant.USERNAME);
usernameConfig.setValue(username);
systemConfigRepository.save(usernameConfig);
String encryptionPassword = SecureUtil.md5(password);
SystemConfig systemConfig = systemConfigRepository.findByKey(SystemConfigConstant.PASSWORD);
systemConfig.setValue(encryptionPassword);
systemConfigRepository.save(systemConfig);
}
public AbstractFileService getCurrentFileService() {
StorageTypeEnum storageStrategy = getCurrentStorageStrategy();
return StorageTypeFactory.getStorageTypeService(storageStrategy);
}
public StorageTypeEnum getCurrentStorageStrategy() {
SystemConfigDTO systemConfigDTO = getSystemConfig();
return systemConfigDTO.getStorageStrategy();
}
public boolean getEnableCache() {
SystemConfigDTO systemConfigDTO = getSystemConfig();
return systemConfigDTO.getEnableCache();
}
}

View File

@@ -1,41 +0,0 @@
package im.zhaojun.common.service;
import im.zhaojun.common.model.constant.ZFileConstant;
import im.zhaojun.common.model.dto.FileItemDTO;
import im.zhaojun.common.model.dto.SiteConfigDTO;
import im.zhaojun.common.util.HttpUtil;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
/**
* @author zhaojun
*/
@Service
public class SystemService {
@Resource
private SystemConfigService systemConfigService;
/**
* 构建指定路径下标题, 页头, 页尾
* @param path 路径
*/
public synchronized SiteConfigDTO getConfig(String path) throws Exception {
SiteConfigDTO siteConfigDTO = new SiteConfigDTO();
AbstractFileService fileService = systemConfigService.getCurrentFileService();
List<FileItemDTO> fileItemList = fileService.fileList(path);
for (FileItemDTO fileItemDTO : fileItemList) {
if (ZFileConstant.FOOTER_FILE_NAME.equalsIgnoreCase(fileItemDTO.getName())) {
siteConfigDTO.setFooter(HttpUtil.getTextContent(fileItemDTO.getUrl()));
} else if (ZFileConstant.HEADER_FILE_NAME.equalsIgnoreCase(fileItemDTO.getName())) {
siteConfigDTO.setHeader(HttpUtil.getTextContent(fileItemDTO.getUrl()));
}
}
return siteConfigDTO;
}
}

View File

@@ -1,84 +0,0 @@
package im.zhaojun.common.util;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.URLUtil;
import cn.hutool.http.HttpUtil;
import com.mpatric.mp3agic.ID3v1;
import com.mpatric.mp3agic.ID3v2;
import com.mpatric.mp3agic.InvalidDataException;
import com.mpatric.mp3agic.Mp3File;
import com.mpatric.mp3agic.UnsupportedTagException;
import im.zhaojun.common.model.constant.ZFileConstant;
import im.zhaojun.common.model.dto.AudioInfoDTO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.net.URL;
/**
* 音频解析工具类
* @author zhaojun
*/
public class AudioHelper {
private static final Logger log = LoggerFactory.getLogger(AudioHelper.class);
public static AudioInfoDTO getAudioInfo(String url) throws Exception {
String query = new URL(URLUtil.decode(url)).getQuery();
if (query != null) {
url = url.replace(query, URLUtil.encode(query));
}
File file = new File(ZFileConstant.USER_HOME + ZFileConstant.AUDIO_TMP_PATH + UUID.fastUUID());
FileUtil.mkParentDirs(file);
HttpUtil.downloadFile(url, file);
AudioInfoDTO audioInfoDTO = parseAudioInfo(file);
audioInfoDTO.setSrc(url);
file.deleteOnExit();
return audioInfoDTO;
}
private static AudioInfoDTO parseAudioInfo(File file) throws IOException, UnsupportedTagException {
AudioInfoDTO audioInfoDTO = new AudioInfoDTO();
audioInfoDTO.setTitle("未知歌曲");
audioInfoDTO.setArtist("未知");
audioInfoDTO.setCover("/shikwasa/audio.png");
Mp3File mp3File = null;
try {
mp3File = new Mp3File(file);
} catch (InvalidDataException e) {
if (log.isDebugEnabled()) {
log.debug("无法解析的音频文件.");
}
}
if (mp3File == null) {
return audioInfoDTO;
}
ID3v1 audioTag = null;
if (mp3File.hasId3v2Tag()) {
ID3v2 id3v2Tag = mp3File.getId3v2Tag();
byte[] albumImage = id3v2Tag.getAlbumImage();
if (albumImage != null) {
audioInfoDTO.setCover("data:" + id3v2Tag.getAlbumImageMimeType() + ";base64," + Base64.encode(albumImage));
}
audioTag = id3v2Tag;
}
if (audioTag != null) {
audioInfoDTO.setTitle(audioTag.getTitle());
audioInfoDTO.setArtist(audioTag.getArtist());
}
return audioInfoDTO;
}
}

View File

@@ -1,49 +0,0 @@
package im.zhaojun.common.util;
import im.zhaojun.common.model.dto.FileItemDTO;
import im.zhaojun.common.model.enums.FileTypeEnum;
import java.util.Comparator;
/**
* 文件比较器
*
* - 文件夹始终比文件排序高
* - 默认按照名称排序
* - 默认排序为升序
* - 按名称排序不区分大小写
*
* @author zhaojun
*/
public class FileComparator implements Comparator<FileItemDTO> {
private String sortBy;
private String order;
public FileComparator(String sortBy, String order) {
this.sortBy = sortBy;
this.order = order;
}
@Override
public int compare(FileItemDTO o1, FileItemDTO o2) {
FileTypeEnum o1Type = o1.getType();
FileTypeEnum o2Type = o2.getType();
if (o1Type.equals(o2Type)) {
int result;
switch (sortBy) {
case "time": result = o1.getTime().compareTo(o2.getTime()); break;
case "size": result = o1.getSize().compareTo(o2.getSize()); break;
default: result = o1.getName().compareToIgnoreCase(o2.getName()); break;
}
return "asc".equals(order) ? result : -result;
}
if (o1Type.equals(FileTypeEnum.FOLDER)) {
return -1;
} else {
return 1;
}
}
}

View File

@@ -1,16 +0,0 @@
package im.zhaojun.common.util;
import org.springframework.web.client.RestTemplate;
/**
* @author zhaojun
*/
public class HttpUtil {
public static String getTextContent(String url) {
RestTemplate restTemplate = SpringContextHolder.getBean(RestTemplate.class);
String result = restTemplate.getForObject(url, String.class);
return result == null ? "" : result;
}
}

View File

@@ -1,70 +0,0 @@
package im.zhaojun.common.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Lazy;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Service;
/**
* @author zhaojun
*/
@Service
@Lazy(false)
public class SpringContextHolder implements ApplicationContextAware, DisposableBean {
private static ApplicationContext applicationContext = null;
private static Logger logger = LoggerFactory.getLogger(SpringContextHolder.class);
/**
* 取得存储在静态变量中的 ApplicationContext.
*/
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
/**
* 从静态变量 applicationContext 中取得 Bean, 自动转型为所赋值对象的类型.
*/
@SuppressWarnings("unchecked")
public static <T> T getBean(String name) {
return (T) applicationContext.getBean(name);
}
/**
* 从静态变量 applicationContext 中取得 Bean, 自动转型为所赋值对象的类型.
*/
public static <T> T getBean(Class<T> requiredType) {
return applicationContext.getBean(requiredType);
}
/**
* 清除 SpringContextHolder 中的 ApplicationContext 为 Null.
*/
public static void clearHolder() {
if (logger.isDebugEnabled()) {
logger.debug("清除SpringContextHolder中的ApplicationContext:" + applicationContext);
}
logger.info("清除SpringContextHolder中的ApplicationContext:" + applicationContext);
applicationContext = null;
}
/**
* 实现 DisposableBean 接口, 在 Context 关闭时清理静态变量.
*/
@Override
public void destroy() {
SpringContextHolder.clearHolder();
}
@Override
public void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException {
SpringContextHolder.applicationContext = applicationContext;
}
}

View File

@@ -1,58 +0,0 @@
package im.zhaojun.common.util;
import cn.hutool.core.net.NetUtil;
import im.zhaojun.common.exception.InitializeException;
import im.zhaojun.common.service.FileAsyncCacheService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.core.env.Environment;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.LinkedHashSet;
/**
* 项目启动监听器, 当项目启动时, 遍历当前对象存储的所有内容, 添加到缓存中.
* @author zhaojun
*/
@Component
@Slf4j
public class StartupListener implements ApplicationListener<ApplicationStartedEvent> {
@Resource
private FileAsyncCacheService fileAsyncCacheService;
@Resource
private Environment environment;
@Override
public void onApplicationEvent(@NonNull ApplicationStartedEvent event) {
printStartInfo();
cacheAllFile();
}
private void printStartInfo() {
String serverPort = environment.getProperty("server.port", "8080");
LinkedHashSet<String> localIps = NetUtil.localIps();
StringBuilder indexAddr = new StringBuilder();
StringBuilder indexAdminAddr = new StringBuilder();
for (String localIp : localIps) {
String addr = String.format("http://%s:%s", localIp, serverPort);
indexAddr.append(addr).append("\t");
indexAdminAddr.append(addr).append("/#/admin").append("\t");
}
log.info("ZFile started at " + indexAddr);
log.info("ZFile Admin started at " + indexAdminAddr);
}
private void cacheAllFile() {
try {
fileAsyncCacheService.cacheGlobalFile();
} catch (Exception e) {
throw new InitializeException("初始化缓存异常.", e);
}
}
}

View File

@@ -1,91 +0,0 @@
package im.zhaojun.common.util;
/**
* @author zhaojun
*/
public class StringUtils {
public static final char DELIMITER = '/';
public static final String HTTP_PROTOCAL = "http://";
public static final String HTTPS_PROTOCAL = "https://";
/**
* 移除 URL 中的第一个 '/'
* @return 如 path = '/folder1/file1', 返回 'folder1/file1'
*/
public static String removeFirstSeparator(String path) {
if (!"".equals(path) && path.charAt(0) == DELIMITER) {
path = path.substring(1);
}
return path;
}
/**
* 移除 URL 中的最后一个 '/'
* @return 如 path = '/folder1/file1/', 返回 '/folder1/file1'
*/
public static String removeLastSeparator(String path) {
if (!"".equals(path) && path.charAt(path.length() - 1) == DELIMITER) {
path = path.substring(0, path.length() - 1);
}
return path;
}
public static String concatUrl(String path, String name) {
return removeDuplicateSeparator(DELIMITER + path + DELIMITER + name);
}
/**
* 将域名和路径组装成 URL, 主要用来处理分隔符 '/'
* @param domain 域名
* @param path 路径
* @return URL
*/
public static String concatPath(String domain, String path) {
if (path != null && path.length() > 1 && path.charAt(0) != DELIMITER) {
path = DELIMITER + path;
}
if (domain.charAt(domain.length() - 1) == DELIMITER) {
domain = domain.substring(0, domain.length() - 2);
}
return domain + path;
}
public static String removeDuplicateSeparator(String path) {
if (path == null || path.length() < 2) {
return path;
}
StringBuilder sb = new StringBuilder();
if (path.indexOf(HTTP_PROTOCAL) == 0) {
sb.append(HTTP_PROTOCAL);
} else if (path.indexOf(HTTPS_PROTOCAL) == 0) {
sb.append(HTTPS_PROTOCAL);
}
for (int i = sb.length(); i < path.length() - 1; i++) {
char current = path.charAt(i);
char next = path.charAt(i + 1);
if (!(current == DELIMITER && next == DELIMITER)) {
sb.append(current);
}
}
sb.append(path.charAt(path.length() - 1));
return sb.toString();
}
public static boolean isNullOrEmpty(String s) {
return s == null || "".equals(s);
}
public static boolean isNotNullOrEmpty(String s) {
return !isNullOrEmpty(s);
}
}

View File

@@ -1,90 +0,0 @@
package im.zhaojun.ftp.service;
import cn.hutool.core.util.URLUtil;
import cn.hutool.extra.ftp.Ftp;
import im.zhaojun.common.model.StorageConfig;
import im.zhaojun.common.model.constant.StorageConfigConstant;
import im.zhaojun.common.model.dto.FileItemDTO;
import im.zhaojun.common.model.enums.FileTypeEnum;
import im.zhaojun.common.model.enums.StorageTypeEnum;
import im.zhaojun.common.service.AbstractFileService;
import im.zhaojun.common.service.FileService;
import im.zhaojun.common.service.StorageConfigService;
import im.zhaojun.common.util.StringUtils;
import org.apache.commons.net.ftp.FTPFile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @author zhaojun
*/
@Service
public class FtpServiceImpl extends AbstractFileService implements FileService {
private static final Logger log = LoggerFactory.getLogger(FtpServiceImpl.class);
@Resource
private StorageConfigService storageConfigService;
private Ftp ftp;
private String domain;
@Override
public void init() {
try {
Map<String, StorageConfig> stringStorageConfigMap =
storageConfigService.selectStorageConfigMapByKey(StorageTypeEnum.FTP);
String host = stringStorageConfigMap.get(StorageConfigConstant.HOST_KEY).getValue();
String port = stringStorageConfigMap.get(StorageConfigConstant.PORT_KEY).getValue();
String username = stringStorageConfigMap.get(StorageConfigConstant.USERNAME_KEY).getValue();
String password = stringStorageConfigMap.get(StorageConfigConstant.PASSWORD_KEY).getValue();
domain = stringStorageConfigMap.get(StorageConfigConstant.DOMAIN_KEY).getValue();
ftp = new Ftp(host, Integer.parseInt(port), username, password);
isInitialized = testConnection();
} catch (Exception e) {
log.debug(getStorageTypeEnum().getDescription() + "初始化异常, 已跳过");
}
}
@Override
@Cacheable
public List<FileItemDTO> fileList(String path) {
FTPFile[] ftpFiles = ftp.lsFiles(path);
List<FileItemDTO> fileItemList = new ArrayList<>();
for (FTPFile ftpFile : ftpFiles) {
FileItemDTO fileItemDTO = new FileItemDTO();
fileItemDTO.setName(ftpFile.getName());
fileItemDTO.setSize(ftpFile.getSize());
fileItemDTO.setTime(ftpFile.getTimestamp().getTime());
fileItemDTO.setType(ftpFile.isDirectory() ? FileTypeEnum.FOLDER : FileTypeEnum.FILE);
fileItemDTO.setPath(path);
if (ftpFile.isFile()) {
fileItemDTO.setUrl(getDownloadUrl(StringUtils.concatUrl(path, fileItemDTO.getName())));
}
fileItemList.add(fileItemDTO);
}
return fileItemList;
}
@Override
@Cacheable
public String getDownloadUrl(String path) {
return URLUtil.complateUrl(domain, path);
}
@Override
public StorageTypeEnum getStorageTypeEnum() {
return StorageTypeEnum.FTP;
}
}

View File

@@ -1,54 +0,0 @@
package im.zhaojun.huawei.service;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.client.builder.AwsClientBuilder;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import im.zhaojun.common.model.StorageConfig;
import im.zhaojun.common.model.constant.StorageConfigConstant;
import im.zhaojun.common.model.enums.StorageTypeEnum;
import im.zhaojun.common.service.AbstractS3FileService;
import im.zhaojun.common.service.FileService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import java.util.Map;
/**
* @author zhaojun
*/
@Service
public class HuaweiServiceImpl extends AbstractS3FileService implements FileService {
private static final Logger log = LoggerFactory.getLogger(HuaweiServiceImpl.class);
@Override
public void init() {
try {
Map<String, StorageConfig> stringStorageConfigMap = storageConfigService.selectStorageConfigMapByKey(StorageTypeEnum.HUAWEI);
String accessKey = stringStorageConfigMap.get(StorageConfigConstant.ACCESS_KEY).getValue();
String secretKey = stringStorageConfigMap.get(StorageConfigConstant.SECRET_KEY).getValue();
String endPoint = stringStorageConfigMap.get(StorageConfigConstant.ENDPOINT_KEY).getValue();
bucketName = stringStorageConfigMap.get(StorageConfigConstant.BUCKET_NAME_KEY).getValue();
domain = stringStorageConfigMap.get(StorageConfigConstant.DOMAIN_KEY).getValue();
basePath = stringStorageConfigMap.get(StorageConfigConstant.BASE_PATH).getValue();
BasicAWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey);
s3Client = AmazonS3ClientBuilder.standard()
.withCredentials(new AWSStaticCredentialsProvider(credentials))
.withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(endPoint, "obs")).build();
isInitialized = testConnection();
} catch (Exception e) {
log.debug(getStorageTypeEnum().getDescription() + "初始化异常, 已跳过");
}
}
@Override
public StorageTypeEnum getStorageTypeEnum() {
return StorageTypeEnum.HUAWEI;
}
}

View File

@@ -1,62 +0,0 @@
package im.zhaojun.local.controller;
import cn.hutool.core.util.URLUtil;
import im.zhaojun.common.util.StringUtils;
import im.zhaojun.local.service.LocalServiceImpl;
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.HandlerMapping;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.util.Date;
/**
* @author zhaojun
*/
@Controller
public class LocalController {
@Resource
private LocalServiceImpl localServiceImpl;
@GetMapping("/file/**")
@ResponseBody
public ResponseEntity<FileSystemResource> downAttachment(final HttpServletRequest request) {
String path = (String) request.getAttribute(
HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
String bestMatchPattern = (String ) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
AntPathMatcher apm = new AntPathMatcher();
String filePath = apm.extractPathWithinPattern(bestMatchPattern, path);
return export(new File(StringUtils.concatPath(localServiceImpl.getFilePath(), URLUtil.decode(filePath))));
}
private ResponseEntity<FileSystemResource> export(File file) {
MediaType mediaType = MediaType.APPLICATION_OCTET_STREAM;
HttpHeaders headers = new HttpHeaders();
headers.add("Cache-Control", "no-cache, no-store, must-revalidate");
headers.setContentDispositionFormData("attachment", URLUtil.encode(file.getName()));
headers.add("Pragma", "no-cache");
headers.add("Expires", "0");
headers.add("Last-Modified", new Date().toString());
headers.add("ETag", String.valueOf(System.currentTimeMillis()));
return ResponseEntity
.ok()
.headers(headers)
.contentLength(file.length())
.contentType(mediaType)
.body(new FileSystemResource(file));
}
}

View File

@@ -1,101 +0,0 @@
package im.zhaojun.local.service;
import im.zhaojun.common.model.StorageConfig;
import im.zhaojun.common.model.SystemConfig;
import im.zhaojun.common.model.constant.StorageConfigConstant;
import im.zhaojun.common.model.constant.SystemConfigConstant;
import im.zhaojun.common.model.dto.FileItemDTO;
import im.zhaojun.common.model.enums.FileTypeEnum;
import im.zhaojun.common.model.enums.StorageTypeEnum;
import im.zhaojun.common.repository.SystemConfigRepository;
import im.zhaojun.common.service.AbstractFileService;
import im.zhaojun.common.service.FileService;
import im.zhaojun.common.service.StorageConfigService;
import im.zhaojun.common.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.io.File;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* @author zhaojun
*/
@Service
public class LocalServiceImpl extends AbstractFileService implements FileService {
private static final Logger log = LoggerFactory.getLogger(LocalServiceImpl.class);
@Resource
private StorageConfigService storageConfigService;
@Resource
private SystemConfigRepository systemConfigRepository;
private String filePath;
@Override
public void init() {
try {
Map<String, StorageConfig> stringStorageConfigMap =
storageConfigService.selectStorageConfigMapByKey(StorageTypeEnum.LOCAL);
filePath = stringStorageConfigMap.get(StorageConfigConstant.FILE_PATH_KEY).getValue();
isInitialized = testConnection();
} catch (Exception e) {
log.debug(getStorageTypeEnum().getDescription() + "初始化异常, 已跳过");
}
}
@Override
public List<FileItemDTO> fileList(String path) {
List<FileItemDTO> fileItemList = new ArrayList<>();
String fullPath = StringUtils.concatPath(filePath, path);
File file = new File(fullPath);
File[] files = file.listFiles();
if (files == null) {
return fileItemList;
}
for (File f : files) {
FileItemDTO fileItemDTO = new FileItemDTO();
fileItemDTO.setType(f.isDirectory() ? FileTypeEnum.FOLDER : FileTypeEnum.FILE);
fileItemDTO.setTime(new Date(f.lastModified()));
fileItemDTO.setSize(f.length());
fileItemDTO.setName(f.getName());
fileItemDTO.setPath(path);
if (f.isFile()) {
fileItemDTO.setUrl(getDownloadUrl(StringUtils.concatUrl(path, f.getName())));
}
fileItemList.add(fileItemDTO);
}
return fileItemList;
}
@Override
public String getDownloadUrl(String path) {
SystemConfig usernameConfig = systemConfigRepository.findByKey(SystemConfigConstant.DOMAIN);
return StringUtils.removeDuplicateSeparator(usernameConfig.getValue() + "/file/" + path);
}
public String getFilePath() {
return filePath;
}
public void setFilePath(String filePath) {
this.filePath = filePath;
}
@Override
public StorageTypeEnum getStorageTypeEnum() {
return StorageTypeEnum.LOCAL;
}
}

View File

@@ -1,54 +0,0 @@
package im.zhaojun.minio;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.client.builder.AwsClientBuilder;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import im.zhaojun.common.model.StorageConfig;
import im.zhaojun.common.model.constant.StorageConfigConstant;
import im.zhaojun.common.model.enums.StorageTypeEnum;
import im.zhaojun.common.service.AbstractS3FileService;
import im.zhaojun.common.service.FileService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import java.util.Map;
/**
* @author zhaojun
*/
@Service
public class MinIOServiceImpl extends AbstractS3FileService implements FileService {
private static final Logger log = LoggerFactory.getLogger(MinIOServiceImpl.class);
@Override
public void init() {
try {
Map<String, StorageConfig> stringStorageConfigMap = storageConfigService.selectStorageConfigMapByKey(StorageTypeEnum.MINIO);
String accessKey = stringStorageConfigMap.get(StorageConfigConstant.ACCESS_KEY).getValue();
String secretKey = stringStorageConfigMap.get(StorageConfigConstant.SECRET_KEY).getValue();
String endPoint = stringStorageConfigMap.get(StorageConfigConstant.ENDPOINT_KEY).getValue();
bucketName = stringStorageConfigMap.get(StorageConfigConstant.BUCKET_NAME_KEY).getValue();
basePath = stringStorageConfigMap.get(StorageConfigConstant.BASE_PATH).getValue();
BasicAWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey);
s3Client = AmazonS3ClientBuilder.standard()
.withCredentials(new AWSStaticCredentialsProvider(credentials))
.withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(endPoint, "minio")).build();
isInitialized = testConnection();
} catch (Exception e) {
log.debug(getStorageTypeEnum().getDescription() + "初始化异常, 已跳过");
}
}
@Override
public StorageTypeEnum getStorageTypeEnum() {
return StorageTypeEnum.MINIO;
}
}

View File

@@ -1,35 +0,0 @@
// package im.zhaojun.onedrive.config;
//
// import org.nuxeo.onedrive.client.*;
// import org.springframework.context.annotation.Configuration;
//
// @Configuration
// public class OneDriveConfig {
//
//
// public void a () {
// OneDriveAPI api = new OneDriveBasicAPI("YOUR_ACCESS_TOKEN");
//
// OneDriveFolder folder = new OneDriveFolder(api, "FOLDER_ID");
// OneDriveFile file = new OneDriveFile(api, "FILE_ID");
// }
//
// public static void main(String[] args) throws OneDriveAPIException {
// OneDriveBasicAPI api = new OneDriveBasicAPI("EwAgA61DBAAUcSSzoTJJsy+XrnQXgAKO5cj4yc8AAQ0ZknDUY8YnwB9aJv7vA9YjiRAVMnKc+rG11fSXLZRAA8Q/CgJaz+OkRN60vaLDfp6KxbmVlob6kxeD/peOUI2eHtk0055Q2+n057tlyVAvGIFl9dvqkItoAthjmybcSkKBZS5h1meWxQ5IOvzSVrdgCKL0NOtTxfh33ZUDsYjvSid6NOX4Bs+pRjvZhQkvqEfGt8KlOL+JoIowmv2I+u09iDmS60BMwSoeK2K3CCLIXxLaiiPYUMsrNk65j4PWEBwBEfyHb6j3lrM/YvwFLq7Y8KJVjrXjFENC7ruja6Ko/cfTMX90yLkUEckpsZ30E6RJHWEHt7jXtNwndDZVknYDZgAACL5pnk17FJfb8AGGxJL1Y0CnAzgkTM2gw+WkFRRDDNzujuW1LQofwZ119HdeANhPrBZ14x32VaPGL1l0RvtR9LCeAN+EogcV5xhVpmCExitaXQB6OkZ6BnXaxLj5TNvFRNeZq0ZfJ3T08clLA1vXHkZhNKgiFDI8xUbahy4r6QpzgoF+0+dz+MA1NzQCQCsRGieS63OD1BKrzRsNxzls5Z9rKzBT6CpWpiaiOg4mmW0yeino/L9zz9Gf5kAJr813bpNr+rH/E8MPd0pZf+6hv37FaVCM7RN1V7CkkCDnRAxwxEK8pDgZhRjZOw7gKutPOiOoTO9ptjh2Jcrds714HitX2HI3RsRY+yyAOcb8XI27m4daSEGCJCuu/TJwXTE4ul54MWsi8MrcDlZN9DOjckiJIqVI8IbvhM+OUAP4FUIfZJJrIVa8WFwxcsMmjlLTxp/I7+JfdvZjJSk3j1yYvbWFviyoSkpQgw2hIDhZxCg083Z6qS467g5H9Uz3fQc+Ss0K0Mud6RcZTU9RqCcp+h92tUc8+gDxQ2NwJsG5vcmSRwf5KHKvsWjt6yK4OHxCpkLYi31eJZtv2EjQGXX1gYyhc/2wQ+cHPvbgBzIfhXetbZKpSxoowAQO/J1i5oRs90h24kjTd4qJd3qspxk1lhZcEC8IkfZXjNgjEQI=");
//
// OneDriveFolder folder = new OneDriveFolder(api, "PDF");
//
// for (OneDriveItem.Metadata metadata : OneDriveFolder.getRoot(api)) {
// System.out.println(metadata.getName());
// if (metadata.isFile()) {
// }
// }
//
// // for (OneDriveItem.Metadata search : OneDriveFolder.getRoot(api).search("index.html")) {
// // System.out.println(search.getName());
// // }
//
// // OneDriveFile file = new OneDriveFile(api, "key.txt");
// // System.out.println(file);
// }
// }

View File

@@ -1,32 +0,0 @@
package im.zhaojun.onedrive.controller;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
/**
* @author zhaojun
*/
@Controller
public class OneDriveController {
@GetMapping("/onedirve/callback")
@ResponseBody
public String onedriveCallback(String code, HttpServletRequest request) {
String json = "client_id=04a73532-6c16-4fe4-92e5-f2cd125ed553&redirect_uri=http://localhost:8080/onedirve/callback&client_secret=2gY/t?*Eff6i36TgKTtiG*08/k]@.I4[&code=" + code + "&grant_type=authorization_code";
HttpRequest post = HttpUtil.createPost("https://login.microsoftonline.com/common/oauth2/v2.0/token");
post.body(json, "application/x-www-form-urlencoded");
HttpResponse response = post.execute();
System.out.println(response.body());
return response.body();
}
}

View File

@@ -1,55 +0,0 @@
package im.zhaojun.qiniu.service;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.client.builder.AwsClientBuilder;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import im.zhaojun.common.model.StorageConfig;
import im.zhaojun.common.model.constant.StorageConfigConstant;
import im.zhaojun.common.model.enums.StorageTypeEnum;
import im.zhaojun.common.service.AbstractS3FileService;
import im.zhaojun.common.service.FileService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.util.Map;
/**
* @author zhaojun
*/
@Service
public class QiniuServiceImpl extends AbstractS3FileService implements FileService {
private static final Logger log = LoggerFactory.getLogger(QiniuServiceImpl.class);
@Override
public void init() {
try {
Map<String, StorageConfig> stringStorageConfigMap = storageConfigService.selectStorageConfigMapByKey(StorageTypeEnum.QINIU);
String accessKey = stringStorageConfigMap.get(StorageConfigConstant.ACCESS_KEY).getValue();
String secretKey = stringStorageConfigMap.get(StorageConfigConstant.SECRET_KEY).getValue();
String endPoint = stringStorageConfigMap.get(StorageConfigConstant.ENDPOINT_KEY).getValue();
bucketName = stringStorageConfigMap.get(StorageConfigConstant.BUCKET_NAME_KEY).getValue();
domain = stringStorageConfigMap.get(StorageConfigConstant.DOMAIN_KEY).getValue();
basePath = stringStorageConfigMap.get(StorageConfigConstant.BASE_PATH).getValue();
BasicAWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey);
s3Client = AmazonS3ClientBuilder.standard()
.withCredentials(new AWSStaticCredentialsProvider(credentials))
.withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(endPoint, "kodo")).build();
isInitialized = testConnection();
} catch (Exception e) {
log.debug(getStorageTypeEnum().getDescription() + "初始化异常, 已跳过");
}
}
@Override
public StorageTypeEnum getStorageTypeEnum() {
return StorageTypeEnum.QINIU;
}
}

View File

@@ -1,55 +0,0 @@
package im.zhaojun.tencent;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.client.builder.AwsClientBuilder;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import im.zhaojun.common.model.StorageConfig;
import im.zhaojun.common.model.constant.StorageConfigConstant;
import im.zhaojun.common.model.enums.StorageTypeEnum;
import im.zhaojun.common.service.AbstractS3FileService;
import im.zhaojun.common.service.FileService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import java.util.Map;
/**
* @author zhaojun
*/
@Service
public class TencentServiceImpl extends AbstractS3FileService implements FileService {
private static final Logger log = LoggerFactory.getLogger(TencentServiceImpl.class);
@Override
public void init() {
try {
Map<String, StorageConfig> stringStorageConfigMap = storageConfigService.selectStorageConfigMapByKey(StorageTypeEnum.TENCENT);
String secretId = stringStorageConfigMap.get(StorageConfigConstant.SECRET_ID_KEY).getValue();
String secretKey = stringStorageConfigMap.get(StorageConfigConstant.SECRET_KEY).getValue();
String regionName = stringStorageConfigMap.get(StorageConfigConstant.ENDPOINT_KEY).getValue();
bucketName = stringStorageConfigMap.get(StorageConfigConstant.BUCKET_NAME_KEY).getValue();
domain = stringStorageConfigMap.get(StorageConfigConstant.DOMAIN_KEY).getValue();
basePath = stringStorageConfigMap.get(StorageConfigConstant.BASE_PATH).getValue();
BasicAWSCredentials credentials = new BasicAWSCredentials(secretId, secretKey);
s3Client = AmazonS3ClientBuilder.standard()
.withCredentials(new AWSStaticCredentialsProvider(credentials))
.withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(regionName, "cos")).build();
isInitialized = testConnection();
} catch (Exception e) {
log.debug(getStorageTypeEnum().getDescription() + "初始化异常, 已跳过");
}
}
@Override
public StorageTypeEnum getStorageTypeEnum() {
return StorageTypeEnum.TENCENT;
}
}

View File

@@ -1,107 +0,0 @@
package im.zhaojun.upyun.service;
import cn.hutool.core.util.URLUtil;
import com.UpYun;
import im.zhaojun.common.model.StorageConfig;
import im.zhaojun.common.model.constant.StorageConfigConstant;
import im.zhaojun.common.model.dto.FileItemDTO;
import im.zhaojun.common.model.enums.FileTypeEnum;
import im.zhaojun.common.model.enums.StorageTypeEnum;
import im.zhaojun.common.service.AbstractFileService;
import im.zhaojun.common.service.FileService;
import im.zhaojun.common.service.StorageConfigService;
import im.zhaojun.common.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author zhaojun
*/
@Service
public class UpYunServiceImpl extends AbstractFileService implements FileService {
private static final Logger log = LoggerFactory.getLogger(UpYunServiceImpl.class);
private static final String END_MARK = "g2gCZAAEbmV4dGQAA2VvZg";
@Resource
private StorageConfigService storageConfigService;
private String domain;
private UpYun upYun;
private String basePath;
@Override
public void init() {
try {
Map<String, StorageConfig> stringStorageConfigMap =
storageConfigService.selectStorageConfigMapByKey(StorageTypeEnum.UPYUN);
String bucketName = stringStorageConfigMap.get(StorageConfigConstant.BUCKET_NAME_KEY).getValue();
String username = stringStorageConfigMap.get(StorageConfigConstant.USERNAME_KEY).getValue();
String password = stringStorageConfigMap.get(StorageConfigConstant.PASSWORD_KEY).getValue();
domain = stringStorageConfigMap.get(StorageConfigConstant.DOMAIN_KEY).getValue();
basePath = stringStorageConfigMap.get(StorageConfigConstant.BASE_PATH).getValue();
upYun = new UpYun(bucketName, username, password);
isInitialized = testConnection();
} catch (Exception e) {
log.debug(getStorageTypeEnum().getDescription() + "初始化异常, 已跳过");
}
}
@Override
@Cacheable
public List<FileItemDTO> fileList(String path) throws Exception {
ArrayList<FileItemDTO> fileItemList = new ArrayList<>();
String nextMark = null;
do {
HashMap<String, String> hashMap = new HashMap<>(24);
hashMap.put("x-list-iter", nextMark);
hashMap.put("x-list-limit", "100");
UpYun.FolderItemIter folderItemIter = upYun.readDirIter(URLUtil.encode(basePath + path), hashMap);
nextMark = folderItemIter.iter;
ArrayList<UpYun.FolderItem> folderItems = folderItemIter.files;
if (folderItems != null) {
for (UpYun.FolderItem folderItem : folderItems) {
FileItemDTO fileItemDTO = new FileItemDTO();
fileItemDTO.setName(folderItem.name);
fileItemDTO.setSize(folderItem.size);
fileItemDTO.setTime(folderItem.date);
fileItemDTO.setPath(path);
if ("folder".equals(folderItem.type)) {
fileItemDTO.setType(FileTypeEnum.FOLDER);
} else {
fileItemDTO.setType(FileTypeEnum.FILE);
fileItemDTO.setUrl(getDownloadUrl(StringUtils.concatUrl(basePath + path, fileItemDTO.getName())));
}
fileItemList.add(fileItemDTO);
}
}
} while (!END_MARK.equals(nextMark));
return fileItemList;
}
@Override
@Cacheable
public String getDownloadUrl(String path) {
return URLUtil.complateUrl(domain, path);
}
@Override
public StorageTypeEnum getStorageTypeEnum() {
return StorageTypeEnum.UPYUN;
}
}

View File

@@ -0,0 +1,23 @@
package im.zhaojun.zfile;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
/**
* @author zhaojun
*/
@SpringBootApplication
@EnableAspectJAutoProxy(exposeProxy = true, proxyTargetClass = true)
@ServletComponentScan(basePackages = {"im.zhaojun.zfile.core.filter", "im.zhaojun.zfile.module.storage.filter"})
@ComponentScan(basePackages = "im.zhaojun.zfile.*")
public class ZfileApplication {
public static void main(String[] args) {
SpringApplication.run(ZfileApplication.class, args);
}
}

View File

@@ -0,0 +1,67 @@
package im.zhaojun.zfile.core;
import im.zhaojun.zfile.core.util.AjaxJson;
import org.slf4j.MDC;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter;
import org.springframework.http.converter.json.MappingJacksonValue;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
/**
* @author zhaojun
*/
@ControllerAdvice
public class CommonResultControllerAdvice implements ResponseBodyAdvice<Object> {
@Override
public boolean supports(MethodParameter returnType,
@NonNull Class<? extends HttpMessageConverter<?>> converterType) {
return AbstractJackson2HttpMessageConverter.class.isAssignableFrom(converterType);
}
@Override
@NonNull
public final Object beforeBodyWrite(@Nullable Object body,
@NonNull MethodParameter returnType,
@NonNull MediaType contentType,
@NonNull Class<? extends HttpMessageConverter<?>> converterType,
@NonNull ServerHttpRequest request,
@NonNull ServerHttpResponse response) {
MappingJacksonValue container = getOrCreateContainer(body);
// The contain body will never be null
beforeBodyWriteInternal(container, contentType, returnType, request, response);
return container;
}
/**
* Wrap the body in a {@link MappingJacksonValue} value container (for providing
* additional serialization instructions) or simply cast it if already wrapped.
*/
private MappingJacksonValue getOrCreateContainer(Object body) {
return body instanceof MappingJacksonValue ? (MappingJacksonValue) body :
new MappingJacksonValue(body);
}
private void beforeBodyWriteInternal(MappingJacksonValue bodyContainer,
MediaType contentType,
MethodParameter returnType,
ServerHttpRequest request,
ServerHttpResponse response) {
// Get return body
Object returnBody = bodyContainer.getValue();
if (returnBody instanceof AjaxJson) {
// If the return body is instance of BaseResponse, then just do nothing
AjaxJson<?> baseResponse = (AjaxJson<?>) returnBody;
baseResponse.setTraceId(MDC.get("traceId"));
}
}
}

View File

@@ -0,0 +1 @@
package im.zhaojun.zfile.core.config;

View File

@@ -0,0 +1,83 @@
package im.zhaojun.zfile.core.config;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.jackson.JsonComponent;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Objects;
/**
* Jackson 枚举反序列化器
*
* @author zhaojun
*/
@Slf4j
@Setter
@JsonComponent
public class JacksonEnumDeserializer extends JsonDeserializer<Enum<?>> implements ContextualDeserializer {
private Class<?> clazz;
/**
* 反序列化操作
*
* @param jsonParser
* json 解析器
*
* @param ctx
* 反序列化上下文
*
* @return 反序列化后的枚举值
* @throws IOException 反序列化异常
*/
@Override
public Enum<?> deserialize(JsonParser jsonParser, DeserializationContext ctx) throws IOException {
Class<?> enumType = clazz;
if (Objects.isNull(enumType) || !enumType.isEnum()) {
return null;
}
String text = jsonParser.getText();
Method method = StringToEnumConverterFactory.getMethod(clazz);
Enum<?>[] enumConstants = (Enum<?>[]) enumType.getEnumConstants();
// 将值与枚举对象对应并缓存
for (Enum<?> e : enumConstants) {
try {
if (Objects.equals(method.invoke(e).toString(), text)) {
return e;
}
} catch (IllegalAccessException | InvocationTargetException ex) {
log.error("获取枚举值错误!!! ", ex);
}
}
return null;
}
/**
* 为不同的枚举获取合适的解析器
*
* @param ctx
* 反序列化上下文
*
* @param property
* property
*/
@Override
public JsonDeserializer<Enum<?>> createContextual(DeserializationContext ctx, BeanProperty property) {
Class<?> rawCls = ctx.getContextualType().getRawClass();
JacksonEnumDeserializer converter = new JacksonEnumDeserializer();
converter.setClazz(rawCls);
return converter;
}
}

View File

@@ -0,0 +1 @@
package im.zhaojun.zfile.core.config;

View File

@@ -0,0 +1,60 @@
package im.zhaojun.zfile.core.config;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.sql.DataSource;
import java.sql.SQLException;
/**
* mybatis-plus 配置类
*
* @author zhaojun
*/
@Configuration
public class MyBatisPlusConfig {
@Resource
private DataSource dataSource;
@Value("${spring.datasource.driver-class-name}")
private String datasourceDriveClassName;
@Value("${spring.datasource.url}")
private String datasourceUrl;
/**
* 如果是 sqlite 数据库,自动创建数据库文件所在目录
*/
@PostConstruct
public void init() {
if (StrUtil.equals(datasourceDriveClassName, "org.sqlite.JDBC")) {
String path = datasourceUrl.replace("jdbc:sqlite:", "");
String folderPath = FileUtil.getParent(path, 1);
if (!FileUtil.exist(folderPath)) {
FileUtil.mkdir(folderPath);
}
}
}
/**
* mybatis plus 分页插件配置
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() throws SQLException {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
String databaseProductName = dataSource.getConnection().getMetaData().getDatabaseProductName();
DbType dbType = DbType.getDbType(databaseProductName);
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(dbType));
return interceptor;
}
}

View File

@@ -0,0 +1,79 @@
package im.zhaojun.zfile.core.config;
import im.zhaojun.zfile.module.storage.constant.StorageConfigConstant;
import im.zhaojun.zfile.module.storage.model.entity.StorageSourceConfig;
import im.zhaojun.zfile.module.storage.service.StorageSourceConfigService;
import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.client.OkHttp3ClientHttpRequestFactory;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List;
/**
* restTemplate 相关配置
*
* @author zhaojun
*/
@Configuration
public class RestTemplateConfig {
@Resource
private StorageSourceConfigService storageSourceConfigService;
/**
* OneDrive 请求 RestTemplate.
* 获取 header 中的 storageId 来判断到底是哪个存储源 ID, 在请求头中添加 Bearer: Authorization {token} 信息, 用于 API 认证.
*/
@Bean
public RestTemplate oneDriveRestTemplate() {
RestTemplate restTemplate = new RestTemplate();
OkHttp3ClientHttpRequestFactory factory = new OkHttp3ClientHttpRequestFactory();
restTemplate.setRequestFactory(factory);
ClientHttpRequestInterceptor interceptor = (httpRequest, bytes, clientHttpRequestExecution) -> {
HttpHeaders headers = httpRequest.getHeaders();
Integer storageId = Integer.valueOf(((List)headers.get("storageId")).get(0).toString());
StorageSourceConfig accessTokenConfig =
storageSourceConfigService.findByStorageIdAndName(storageId, StorageConfigConstant.ACCESS_TOKEN_KEY);
String tokenValue = String.format("%s %s", "Bearer", accessTokenConfig.getValue());
httpRequest.getHeaders().add("Authorization", tokenValue);
return clientHttpRequestExecution.execute(httpRequest, bytes);
};
restTemplate.setInterceptors(Collections.singletonList(interceptor));
return restTemplate;
}
/**
* restTemplate 设置请求和响应字符集都为 UTF-8, 并设置响应头为 Content-Type: application/text;
*/
@Bean
public RestTemplate restTemplate(){
HttpComponentsClientHttpRequestFactory httpRequestFactory = new HttpComponentsClientHttpRequestFactory();
HttpClient httpClient = HttpClientBuilder.create().build();
httpRequestFactory.setHttpClient(httpClient);
RestTemplate restTemplate = new RestTemplate(httpRequestFactory);
restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));
restTemplate.setInterceptors(Collections.singletonList((request, body, execution) -> {
ClientHttpResponse response = execution.execute(request, body);
HttpHeaders headers = response.getHeaders();
headers.put("Content-Type", Collections.singletonList("application/text"));
return response;
}));
return restTemplate;
}
}

View File

@@ -0,0 +1 @@
package im.zhaojun.zfile.core.config;

View File

@@ -0,0 +1,27 @@
package im.zhaojun.zfile.core.config;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
import org.springframework.cache.transaction.TransactionAwareCacheManagerProxy;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Spring Cache 相关配置
*
* @author zhaojun
*/
@Configuration
@EnableCaching
public class SpringCacheConfig {
/**
* 使用 TransactionAwareCacheManagerProxy 装饰 ConcurrentMapCacheManager使其支持事务 (将 put、evict、clear 操作延迟到事务成功提交再执行.
*/
@Bean
public CacheManager cacheManager() {
return new TransactionAwareCacheManagerProxy(new ConcurrentMapCacheManager());
}
}

View File

@@ -0,0 +1,116 @@
package im.zhaojun.zfile.core.config;
import com.baomidou.mybatisplus.annotation.EnumValue;
import com.baomidou.mybatisplus.annotation.IEnum;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConverterFactory;
import javax.validation.constraints.NotNull;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
/**
* String 转枚举通用转换器工厂
*
* @author zhaojun
*/
@Slf4j
public class StringToEnumConverterFactory implements ConverterFactory<String, Enum<?>> {
/**
* 存储枚举类型的缓存
*/
private static final Map<Class<?>, Converter<String, ? extends Enum<?>>> CONVERTER_MAP = new ConcurrentHashMap<>();
/**
* 枚举类的获取枚举值方法缓存
*/
private static final Map<Class<?>, Method> TABLE_METHOD_OF_ENUM_TYPES = new ConcurrentHashMap<>();
@Override
@SuppressWarnings("unchecked cast")
public <T extends Enum<?>> Converter<String, T> getConverter(Class<T> targetType) {
// 缓存转换器
Converter<String, T> converter = (Converter<String, T>) CONVERTER_MAP.get(targetType);
if (converter == null) {
converter = new StringToEnumConverter<>(targetType);
CONVERTER_MAP.put(targetType, converter);
}
return converter;
}
static class StringToEnumConverter<T extends Enum<?>> implements Converter<String, T> {
private final Map<String, T> enumMap = new ConcurrentHashMap<>();
StringToEnumConverter(Class<T> enumType) {
Method method = getMethod(enumType);
T[] enums = enumType.getEnumConstants();
// 将值与枚举对象对应并缓存
for (T e : enums) {
try {
enumMap.put(method.invoke(e).toString(), e);
} catch (IllegalAccessException | InvocationTargetException ex) {
log.error("获取枚举值错误!!! ", ex);
}
}
}
@Override
public T convert(@NotNull String source) {
// 获取
T t = enumMap.get(source);
if (t == null) {
throw new IllegalArgumentException("该字符串找不到对应的枚举对象 字符串:" + source);
}
return t;
}
}
public static <T> Method getMethod(Class<T> enumType) {
Method method;
// 找到取值的方法
if (IEnum.class.isAssignableFrom(enumType)) {
try {
method = enumType.getMethod("getValue");
} catch (NoSuchMethodException e) {
throw new IllegalArgumentException(String.format("类:%s 找不到 getValue方法",
enumType.getName()));
}
} else {
method = TABLE_METHOD_OF_ENUM_TYPES.computeIfAbsent(enumType, k -> {
Field field =
dealEnumType(enumType).orElseThrow(() -> new IllegalArgumentException(String.format(
"类:%s 找不到 EnumValue注解", enumType.getName())));
Class<?> fieldType = field.getType();
String fieldName = field.getName();
String methodName = StringUtils.concatCapitalize(boolean.class.equals(fieldType) ? "is" : "get", fieldName);
try {
return enumType.getDeclaredMethod(methodName);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
return null;
});
}
return method;
}
private static Optional<Field> dealEnumType(Class<?> clazz) {
return clazz.isEnum() ?
Arrays.stream(clazz.getDeclaredFields()).filter(field -> field.isAnnotationPresent(EnumValue.class)).findFirst() : Optional.empty();
}
}

View File

@@ -0,0 +1,68 @@
package im.zhaojun.zfile.core.config;
import im.zhaojun.zfile.module.storage.model.enums.StorageTypeEnum;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.ConfigurableWebServerFactory;
import org.springframework.boot.web.server.ErrorPage;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.http.HttpStatus;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.HashSet;
import java.util.Set;
/**
* ZFile Web 相关配置.
*
* @author zhaojun
*/
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
/**
* 添加自定义枚举格式化器.
* @see StorageTypeEnum
*/
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverterFactory(new StringToEnumConverterFactory());
}
/**
* 支持 url 中传入 <>[\]^`{|} 这些特殊字符.
*/
@Bean
public ServletWebServerFactory webServerFactory() {
TomcatServletWebServerFactory webServerFactory = new TomcatServletWebServerFactory();
// 添加对 URL 中特殊符号的支持.
webServerFactory.addConnectorCustomizers(connector -> {
connector.setProperty("relaxedPathChars", "<>[\\]^`{|}%[]");
connector.setProperty("relaxedQueryChars", "<>[\\]^`{|}%[]");
});
return webServerFactory;
}
@Bean
public WebServerFactoryCustomizer<ConfigurableWebServerFactory> webServerFactoryCustomizer(){
return factory -> {
ErrorPage error404Page = new ErrorPage(HttpStatus.NOT_FOUND, "/index.html");
ErrorPage error200Page = new ErrorPage(HttpStatus.OK, "/index.html");
Set<ErrorPage> errorPages = new HashSet<>();
errorPages.add(error404Page);
errorPages.add(error200Page);
factory.setErrorPages(errorPages);
};
}
}

View File

@@ -0,0 +1,19 @@
package im.zhaojun.zfile.core.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* @author zhaojun
*/
@Data
@EnableConfigurationProperties
@Component
@ConfigurationProperties(prefix = "zfile")
public class ZFileProperties {
private boolean debug;
}

View File

@@ -0,0 +1,16 @@
package im.zhaojun.zfile.core.constant;
/**
* Slf4j mdc 常量
*
* @author zhaojun
*/
public class MdcConstant {
public static final String TRACE_ID = "traceId";
public static final String IP = "ip";
public static final String USER = "user";
}

View File

@@ -0,0 +1,29 @@
package im.zhaojun.zfile.core.constant;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
/**
* ZFile 常量
*
* @author zhaojun
*/
@Configuration
public class ZFileConstant {
public static final Character PATH_SEPARATOR_CHAR = '/';
public static final String PATH_SEPARATOR = "/";
/**
* 最大支持文本文件大小为 ? KB 的文件内容.
*/
public static Long TEXT_MAX_FILE_SIZE_KB = 100L;
@Autowired(required = false)
public void setTextMaxFileSizeMb(@Value("${zfile.preview.text.maxFileSizeKb}") Long maxFileSizeKb) {
ZFileConstant.TEXT_MAX_FILE_SIZE_KB = maxFileSizeKb;
}
}

View File

@@ -0,0 +1,25 @@
package im.zhaojun.zfile.core.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* 处理前端首页 Controller
*
* @author zhaojun
*/
@Controller
public class FrontIndexController {
/**
* 所有未找到的页面都跳转到首页, 用户解决 vue history 直接访问 404 的问题
*
* @return 转发到 /index.html
*/
@RequestMapping(value = "/**/{[path:[^\\.]*}")
public String redirect() {
// Forward to home page so that route is preserved.
return "forward:/";
}
}

View File

@@ -0,0 +1,47 @@
package im.zhaojun.zfile.core.controller;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ZipUtil;
import com.github.xiaoymin.knife4j.annotations.ApiSort;
import im.zhaojun.zfile.core.util.FileResponseUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.File;
import java.util.Date;
/**
* 获取系统日志接口
*
* @author zhaojun
*/
@Api(tags = "日志")
@ApiSort(8)
@Slf4j
@RestController
@RequestMapping("/admin")
public class LogController {
@Value("${zfile.log.path}")
private String zfileLogPath;
@GetMapping("/log/download")
@ApiOperation(value = "下载系统日志")
public ResponseEntity<Resource> downloadLog() {
if (log.isDebugEnabled()) {
log.debug("下载诊断日志");
}
File fileZip = ZipUtil.zip(zfileLogPath);
String currentDate = DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss");
return FileResponseUtil.exportSingleThread(fileZip, "ZFile 诊断日志 - " + currentDate + ".zip");
}
}

View File

@@ -0,0 +1,14 @@
package im.zhaojun.zfile.core.exception;
/**
* 非法使用下载链接异常.
*
* @author zhaojun
*/
public class IllegalDownloadLinkException extends ZFileRuntimeException {
public IllegalDownloadLinkException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,14 @@
package im.zhaojun.zfile.core.exception;
/**
* 系统初始化异常
*
* @author zhaojun
*/
public class InstallSystemException extends ZFileRuntimeException {
public InstallSystemException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,14 @@
package im.zhaojun.zfile.core.exception;
/**
* 无效的直链异常
*
* @author zhaojun
*/
public class InvalidShortLinkException extends ZFileRuntimeException {
public InvalidShortLinkException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,14 @@
package im.zhaojun.zfile.core.exception;
/**
* 登陆验证码验证异常
*
* @author zhaojun
*/
public class LoginVerifyException extends ZFileRuntimeException {
public LoginVerifyException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,22 @@
package im.zhaojun.zfile.core.exception;
/**
* 密码校验失败异常
*
* @author zhaojun
*/
public class PasswordVerifyException extends RuntimeException {
private final Integer code;
public PasswordVerifyException(Integer code, String message) {
super(message);
this.code = code;
}
public Integer getCode() {
return code;
}
}

View File

@@ -0,0 +1,14 @@
package im.zhaojun.zfile.core.exception;
/**
* 文件预览异常类
*
* @author zhaojun
*/
public class PreviewException extends ZFileRuntimeException {
public PreviewException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,42 @@
package im.zhaojun.zfile.core.exception;
import im.zhaojun.zfile.core.util.CodeMsg;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* Service 层异常
* 所有 message 均为系统日志打印输出, CodeMsg 中的消息才是返回给客户端的消息.
*
* @author zhaojun
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class ServiceException extends RuntimeException {
private CodeMsg codeMsg;
public ServiceException(CodeMsg codeMsg) {
this.codeMsg = codeMsg;
}
public ServiceException(String message, CodeMsg codeMsg) {
super(message);
this.codeMsg = codeMsg;
}
public ServiceException(String message, Throwable cause, CodeMsg codeMsg) {
super(message, cause);
this.codeMsg = codeMsg;
}
public ServiceException(Throwable cause, CodeMsg codeMsg) {
super(cause);
this.codeMsg = codeMsg;
}
public ServiceException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace, CodeMsg codeMsg) {
super(message, cause, enableSuppression, writableStackTrace);
this.codeMsg = codeMsg;
}
}

View File

@@ -0,0 +1,21 @@
package im.zhaojun.zfile.core.exception;
import im.zhaojun.zfile.module.storage.model.param.IStorageParam;
import lombok.Getter;
/**
* 存储源自动设置 cors 异常
*
* @author zhaojun
*/
@Getter
public class StorageSourceAutoConfigCorsException extends RuntimeException {
private final IStorageParam iStorageParam;
public StorageSourceAutoConfigCorsException(String message, Throwable cause, IStorageParam iStorageParam) {
super(message, cause);
this.iStorageParam = iStorageParam;
}
}

View File

@@ -0,0 +1,60 @@
package im.zhaojun.zfile.core.exception;
import im.zhaojun.zfile.core.util.CodeMsg;
import lombok.EqualsAndHashCode;
import lombok.Getter;
/**
* 存储源异常
*
* @author zhaojun
*/
@EqualsAndHashCode(callSuper = true)
@Getter
public class StorageSourceException extends ServiceException {
/**
* 是否使用异常消息进行接口返回,如果是则取异常的 message, 否则取 CodeMsg 中的 message
*/
private boolean responseExceptionMessage;
/**
* 存储源 ID
*/
private final Integer storageId;
public StorageSourceException(CodeMsg codeMsg, Integer storageId, String message) {
super(message, codeMsg);
this.storageId = storageId;
}
public StorageSourceException(CodeMsg codeMsg, Integer storageId, String message, Throwable cause) {
super(message, cause, codeMsg);
this.storageId = storageId;
}
/**
* 根据 responseExceptionMessage 判断使用异常消息进行接口返回,如果是则取异常的 message, 否则取 CodeMsg 中的 message
*
* @return 异常消息
*/
public String getResultMessage() {
return responseExceptionMessage ? super.getMessage() : super.getCodeMsg().getMsg();
}
/**
* 设置值是否使用异常消息进行接口返回
*
* @param responseExceptionMessage
* 是否使用异常消息进行接口返回
*
* @return 当前对象
*/
public StorageSourceException setResponseExceptionMessage(boolean responseExceptionMessage) {
this.responseExceptionMessage = responseExceptionMessage;
return this;
}
}

View File

@@ -0,0 +1,14 @@
package im.zhaojun.zfile.core.exception;
/**
* 存储源不支持代理上传异常
*
* @author zhaojun
*/
public class StorageSourceNotSupportProxyUploadException extends ZFileRuntimeException {
public StorageSourceNotSupportProxyUploadException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,22 @@
package im.zhaojun.zfile.core.exception;
import lombok.Getter;
/**
* @author zhaojun
*/
@Getter
public class StorageSourceRefreshTokenException extends RuntimeException {
private final Integer storageId;
public StorageSourceRefreshTokenException(String message, Integer storageId) {
super(message);
this.storageId = storageId;
}
public StorageSourceRefreshTokenException(String message, Throwable cause, Integer storageId) {
super(message, cause);
this.storageId = storageId;
}
}

View File

@@ -0,0 +1,17 @@
package im.zhaojun.zfile.core.exception;
/**
* 文件解析异常
*
* @author zhaojun
*/
public class TextParseException extends ZFileRuntimeException {
public TextParseException(String message) {
super(message);
}
public TextParseException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@@ -0,0 +1,15 @@
package im.zhaojun.zfile.core.exception;
/**
* @author zhaojun
*/
public class ZFileRuntimeException extends RuntimeException {
public ZFileRuntimeException(String message) {
super(message);
}
public ZFileRuntimeException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@@ -0,0 +1,25 @@
package im.zhaojun.zfile.core.exception.file;
import im.zhaojun.zfile.core.exception.StorageSourceException;
import im.zhaojun.zfile.core.util.CodeMsg;
/**
* 无效的存储源异常
*
* @author zhaojun
*/
public class InvalidStorageSourceException extends StorageSourceException {
public InvalidStorageSourceException(String message) {
super(CodeMsg.STORAGE_SOURCE_NOT_FOUND, null, message);
}
public InvalidStorageSourceException(Integer storageId) {
super(CodeMsg.STORAGE_SOURCE_NOT_FOUND, storageId, CodeMsg.STORAGE_SOURCE_NOT_FOUND.getMsg());
}
public InvalidStorageSourceException(Integer storageId, String message) {
super(CodeMsg.STORAGE_SOURCE_NOT_FOUND, storageId, message);
}
}

View File

@@ -0,0 +1,21 @@
package im.zhaojun.zfile.core.exception.file.init;
import im.zhaojun.zfile.core.exception.StorageSourceException;
import im.zhaojun.zfile.core.util.CodeMsg;
/**
* 存储源初始化异常
*
* @author zhaojun
*/
public class InitializeStorageSourceException extends StorageSourceException {
public InitializeStorageSourceException(CodeMsg codeMsg, Integer storageId, String message) {
super(codeMsg, storageId, message);
}
public InitializeStorageSourceException(CodeMsg codeMsg, Integer storageId, String message, Throwable cause) {
super(codeMsg, storageId, message, cause);
}
}

View File

@@ -0,0 +1,24 @@
package im.zhaojun.zfile.core.exception.file.operator;
import im.zhaojun.zfile.core.exception.StorageSourceException;
import im.zhaojun.zfile.core.util.CodeMsg;
/**
* 禁止服务器代理下载异常
*
* @author zhaojun
*/
public class DisableProxyDownloadException extends StorageSourceException {
public DisableProxyDownloadException(CodeMsg codeMsg, Integer storageId) {
super(codeMsg, storageId, null);
}
public DisableProxyDownloadException(CodeMsg codeMsg, Integer storageId, String message) {
super(codeMsg, storageId, message);
}
public DisableProxyDownloadException(CodeMsg codeMsg, Integer storageId, String message, Throwable cause) {
super(codeMsg, storageId, message, cause);
}
}

View File

@@ -0,0 +1,18 @@
package im.zhaojun.zfile.core.exception.file.operator;
import im.zhaojun.zfile.core.exception.StorageSourceException;
import im.zhaojun.zfile.core.util.CodeMsg;
import lombok.Getter;
/**
* 存储源文件操作异常
* @author zhaojun
*/
@Getter
public class StorageSourceFileOperatorException extends StorageSourceException {
public StorageSourceFileOperatorException(CodeMsg codeMsg, Integer storageId, String message, Throwable cause) {
super(codeMsg, storageId, message, cause);
}
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,12 @@
package im.zhaojun.zfile.core.exception.http;
/**
* Http 请求状态码异常 (返回状态码为 5xx 抛出此异常)
* @author zhaojun
*/
public class HttpResponseStatusErrorException extends RuntimeException {
public HttpResponseStatusErrorException(String message) {
super(message);
}
}

Some files were not shown because too many files have changed in this diff Show More