Compare commits
545 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
90101c173c | ||
|
|
c590953197 | ||
|
|
23772223bd | ||
|
|
b88f413906 | ||
|
|
6d8bdf1df3 | ||
|
|
0173357ec9 | ||
|
|
b4ae4bcc90 | ||
|
|
f7679217ec | ||
|
|
901d539332 | ||
|
|
1b3c284f4e | ||
|
|
f5b6f8da4e | ||
|
|
6da72845e4 | ||
|
|
81b9ac5923 | ||
|
|
8f8dc86365 | ||
|
|
f9d46edbcc | ||
|
|
e3991be3b7 | ||
|
|
d42853abe2 | ||
|
|
e5c6a1aca4 | ||
|
|
b5d46912d2 | ||
|
|
dfcbaf7158 | ||
|
|
17257dccda | ||
|
|
1c1248306c | ||
|
|
d5873a7f97 | ||
|
|
7437dc8936 | ||
|
|
549a599a5b | ||
|
|
38effb0bb7 | ||
|
|
1e25c23b0b | ||
|
|
c870d32777 | ||
|
|
b86e487a47 | ||
|
|
330713509e | ||
|
|
ecf85dfe9e | ||
|
|
ac3b4283a3 | ||
|
|
141f9dee5e | ||
|
|
ce9f809ab5 | ||
|
|
b83af8dc5e | ||
|
|
ce0a7bd6ef | ||
|
|
008425734c | ||
|
|
e078366395 | ||
|
|
69ec12ab99 | ||
|
|
8e93874c69 | ||
|
|
2a6f0f94cc | ||
|
|
d501d96ad6 | ||
|
|
73569a63f8 | ||
|
|
71e6ba4d8b | ||
|
|
72a627be74 | ||
|
|
0344f687b6 | ||
|
|
1b0789cdd0 | ||
|
|
7cf16754f8 | ||
|
|
6aefc107e7 | ||
|
|
e082043f99 | ||
|
|
a55e8ae2ad | ||
|
|
188431b64d | ||
|
|
7f23dcb7c4 | ||
|
|
8dcb64a60d | ||
|
|
d9c64ff369 | ||
|
|
6963b1d593 | ||
|
|
0a61e1047d | ||
|
|
3f02cd9832 | ||
|
|
3aa42c00fa | ||
|
|
77b2253ff6 | ||
|
|
71a4fdfbaf | ||
|
|
2ecd69dc51 | ||
|
|
ebae9ba5c8 | ||
|
|
b2fd722443 | ||
|
|
e2ce404e87 | ||
|
|
6cfbe7689e | ||
|
|
b95c1d890b | ||
|
|
461c77012a | ||
|
|
9328e0ea9d | ||
|
|
aefa928a19 | ||
|
|
89c6c515f1 | ||
|
|
dcadffa265 | ||
|
|
300e58e92c | ||
|
|
96b71e4f8d | ||
|
|
456aabb893 | ||
|
|
b4d2ca238f | ||
|
|
2afb841fd9 | ||
|
|
2b09812153 | ||
|
|
972099a598 | ||
|
|
9335d78d60 | ||
|
|
f332cb929b | ||
|
|
a190a2ec6e | ||
|
|
5bfa7037cb | ||
|
|
4a94c879b8 | ||
|
|
38161f96e1 | ||
|
|
33c751ab33 | ||
|
|
d12cbd2383 | ||
|
|
5f84becf08 | ||
|
|
432fd89c0f | ||
|
|
84c8adc9d2 | ||
|
|
ba2523ac8a | ||
|
|
dcdd25c01f | ||
|
|
278e320550 | ||
|
|
1bfa66cc49 | ||
|
|
20fb2b3baa | ||
|
|
e9a85a4e88 | ||
|
|
63edbc11d3 | ||
|
|
c98c6af0f9 | ||
|
|
47f6066733 | ||
|
|
f4fb471da3 | ||
|
|
301b6bdf70 | ||
|
|
a98c4f4c48 | ||
|
|
f4a625dac6 | ||
|
|
1c2f3de55c | ||
|
|
ed375768f5 | ||
|
|
b0c4ed1fad | ||
|
|
8d30ca7eee | ||
|
|
43aba8e20e | ||
|
|
a2fa8b3eeb | ||
|
|
0ef77ee11b | ||
|
|
6608c0b456 | ||
|
|
3ceb5c8c1b | ||
|
|
7399c89a8e | ||
|
|
477c9dbdd2 | ||
|
|
e8117c7d3b | ||
|
|
b29ff1e646 | ||
|
|
774a8e184a | ||
|
|
7da1405798 | ||
|
|
564770ba3c | ||
|
|
ac07de4e73 | ||
|
|
3933eba99a | ||
|
|
27e8f7961e | ||
|
|
55843cbef6 | ||
|
|
ebbb33409f | ||
|
|
b39360791f | ||
|
|
9833378e25 | ||
|
|
31df2d16e2 | ||
|
|
f8f07912a1 | ||
|
|
edf43954c6 | ||
|
|
5a816b1dfb | ||
|
|
910406c33a | ||
|
|
543f76ad1d | ||
|
|
1cc2d874a1 | ||
|
|
18de372bf9 | ||
|
|
5748644814 | ||
|
|
fb8060d316 | ||
|
|
a4005defe2 | ||
|
|
4c90d5bdda | ||
|
|
fa46850bb4 | ||
|
|
a7551fce53 | ||
|
|
0591669ec9 | ||
|
|
6286a9e9aa | ||
|
|
c67ee1e89d | ||
|
|
c75695c63a | ||
|
|
8f771c652d | ||
|
|
d6e13d6115 | ||
|
|
8460d17b07 | ||
|
|
49806221b4 | ||
|
|
409af8409d | ||
|
|
6647efeb03 | ||
|
|
c8bd52e26a | ||
|
|
27db6ed14a | ||
|
|
b079d03753 | ||
|
|
edf8e114ad | ||
|
|
67a84edd4d | ||
|
|
d798750ee6 | ||
|
|
fea1da86fc | ||
|
|
96a0c90600 | ||
|
|
a054e82740 | ||
|
|
dc0e84e1e3 | ||
|
|
ae31005959 | ||
|
|
0f167a304d | ||
|
|
43d8143c74 | ||
|
|
b2fb1af99b | ||
|
|
a4f4d654a3 | ||
|
|
04d9c24b43 | ||
|
|
91a7092190 | ||
|
|
f82155f157 | ||
|
|
a99218dfa5 | ||
|
|
3e9fe27890 | ||
|
|
7524f58928 | ||
|
|
6499f2d220 | ||
|
|
40b1f1bc2d | ||
|
|
3bd859d705 | ||
|
|
6156d85ba3 | ||
|
|
e1df2f886b | ||
|
|
cea42a57bb | ||
|
|
c123d2a671 | ||
|
|
02e00b765b | ||
|
|
1992c1f52d | ||
|
|
4eb9a721fc | ||
|
|
f1bac40af4 | ||
|
|
0a7fb41e81 | ||
|
|
4fd0b8d442 | ||
|
|
28150b0c1a | ||
|
|
a7813ff7bc | ||
|
|
ae500ef9dc | ||
|
|
29d5b84df2 | ||
|
|
28882b198c | ||
|
|
d624c886fe | ||
|
|
e08654a59e | ||
|
|
8fa6a5c9b8 | ||
|
|
7620a578fb | ||
|
|
9c9bf93f00 | ||
|
|
912eb694cb | ||
|
|
78d5ba90cf | ||
|
|
056f9d4449 | ||
|
|
9a6020dcc8 | ||
|
|
6b3025f379 | ||
|
|
9dffaaee25 | ||
|
|
bda654a012 | ||
|
|
52271b5117 | ||
|
|
223a6c1970 | ||
|
|
60c272b714 | ||
|
|
3b6ed1c78d | ||
|
|
5d888bb68f | ||
|
|
082bb07213 | ||
|
|
803b8cdf71 | ||
|
|
eca5f7e48b | ||
|
|
b70c37f3f0 | ||
|
|
5cb2844141 | ||
|
|
4dd6cdb4b3 | ||
|
|
c6127c029f | ||
|
|
e149039ecb | ||
|
|
37688d83cf | ||
|
|
6692016642 | ||
|
|
3f41aeda9a | ||
|
|
de86d5c47d | ||
|
|
c89e072005 | ||
|
|
b533b5e959 | ||
|
|
d35cf27d47 | ||
|
|
796c4c1fb0 | ||
|
|
1033d6c1a9 | ||
|
|
e09c6b4e58 | ||
|
|
168b0b08f3 | ||
|
|
60a6a5348c | ||
|
|
2ec8a5df1f | ||
|
|
47b5f6ac12 | ||
|
|
c64c8465f2 | ||
|
|
f636681dd8 | ||
|
|
eadd2434e0 | ||
|
|
b84c0bff42 | ||
|
|
4cb5b84bfe | ||
|
|
0351b4401c | ||
|
|
48cb14be8a | ||
|
|
eea2ff11f9 | ||
|
|
325ec1a348 | ||
|
|
93205266d3 | ||
|
|
6849a4347f | ||
|
|
cb5c6a5945 | ||
|
|
5ed45c3bb3 | ||
|
|
4442ec3165 | ||
|
|
b268a24333 | ||
|
|
84f9354d4e | ||
|
|
8dfc4f8004 | ||
|
|
1158f5c2b9 | ||
|
|
c5f0e15207 | ||
|
|
68a842ce75 | ||
|
|
3bd4f74dae | ||
|
|
4a0bdc3baf | ||
|
|
603c1b4654 | ||
|
|
1136d582df | ||
|
|
187544fc06 | ||
|
|
aa3cde8f59 | ||
|
|
e29a702b6e | ||
|
|
e4f663c9f0 | ||
|
|
fb08ef6e78 | ||
|
|
10c465d159 | ||
|
|
d15d1203b7 | ||
|
|
de48ed3b61 | ||
|
|
d22e2e872a | ||
|
|
7da1b454dc | ||
|
|
463f311dd3 | ||
|
|
73b42cf654 | ||
|
|
1fac59c4cd | ||
|
|
6ed6b4a019 | ||
|
|
7bd02437f0 | ||
|
|
57aeb5771c | ||
|
|
695c03a530 | ||
|
|
6ebc403572 | ||
|
|
7ff6fe43b5 | ||
|
|
b34f141181 | ||
|
|
87dd7b58d1 | ||
|
|
2a949db5d2 | ||
|
|
b889e91fb5 | ||
|
|
b5c757f9f0 | ||
|
|
a465f48b94 | ||
|
|
797a0a0e4c | ||
|
|
e7ff159b6d | ||
|
|
a9fbf54bb2 | ||
|
|
81f9e262f5 | ||
|
|
23bb3960ab | ||
|
|
c4a17985a4 | ||
|
|
75ddcd47f4 | ||
|
|
2dd03ae490 | ||
|
|
5b383c8741 | ||
|
|
73198d7852 | ||
|
|
fb0d9721aa | ||
|
|
b24c663fd6 | ||
|
|
6e62cfc84d | ||
|
|
eee22e9dc9 | ||
|
|
5109c51ffc | ||
|
|
66d6d311ea | ||
|
|
ef7cbdcbd7 | ||
|
|
63bcbebb4b | ||
|
|
50ce1bb6db | ||
|
|
3c88659679 | ||
|
|
d79a993eea | ||
|
|
afafb311b8 | ||
|
|
2e280e4931 | ||
|
|
ed6efac8b7 | ||
|
|
7409df85d7 | ||
|
|
4d42529c4d | ||
|
|
65224685c8 | ||
|
|
080a84986e | ||
|
|
3f8beb2f0b | ||
|
|
410a87c426 | ||
|
|
c14b8343d2 | ||
|
|
bea440f6c3 | ||
|
|
6a5fe15121 | ||
|
|
e920ab0ec0 | ||
|
|
537e3e0563 | ||
|
|
e208dc7c4c | ||
|
|
ed64910a53 | ||
|
|
5b075c3505 | ||
|
|
a8e6d9af6a | ||
|
|
2b21d8a73c | ||
|
|
e30289d21b | ||
|
|
3b6e2be7fe | ||
|
|
43c12aa8a7 | ||
|
|
c03a7710c0 | ||
|
|
1833b23d84 | ||
|
|
f3e393972d | ||
|
|
4f46c13369 | ||
|
|
f181959218 | ||
|
|
11effc0ae7 | ||
|
|
c8397e17bf | ||
|
|
ed32b9f1d4 | ||
|
|
4e184936db | ||
|
|
fe6aebfdee | ||
|
|
d65e1a442d | ||
|
|
34647793c8 | ||
|
|
e8c249b9ea | ||
|
|
d1e613dc10 | ||
|
|
1adcfee96f | ||
|
|
75f5de6b9a | ||
|
|
499942ef70 | ||
|
|
e11277ce26 | ||
|
|
5edd9e38a7 | ||
|
|
f4ffee706b | ||
|
|
bb65750278 | ||
|
|
e09167c0d0 | ||
|
|
ee6c04fa11 | ||
|
|
b31982b788 | ||
|
|
544a3d3eb2 | ||
|
|
1987bc97a9 | ||
|
|
7e878af06c | ||
|
|
766a047ee1 | ||
|
|
c1d29a46f5 | ||
|
|
08e39b3d15 | ||
|
|
e7790ac256 | ||
|
|
499f3e108c | ||
|
|
19144b653e | ||
|
|
17a87648fa | ||
|
|
ac4cef0980 | ||
|
|
71978f8003 | ||
|
|
0b3a67ec6e | ||
|
|
ee5fb54ebb | ||
|
|
4585f22817 | ||
|
|
b523453588 | ||
|
|
a8cc03c911 | ||
|
|
949c437653 | ||
|
|
84e9cce60f | ||
|
|
5720bd93ec | ||
|
|
bcae9713bc | ||
|
|
04e3023071 | ||
|
|
a09ef84629 | ||
|
|
1d29395191 | ||
|
|
3720dc6aa9 | ||
|
|
3ada172be2 | ||
|
|
15f8fbb49b | ||
|
|
547e688d38 | ||
|
|
708eb33d0e | ||
|
|
e954b725b1 | ||
|
|
c89cb4e495 | ||
|
|
286e9775f6 | ||
|
|
60513abe6a | ||
|
|
f7a8c9faa2 | ||
|
|
55e0d32ef8 | ||
|
|
4c0bacba31 | ||
|
|
61128f2677 | ||
|
|
3866526b95 | ||
|
|
250955fac9 | ||
|
|
3249266cd1 | ||
|
|
1a6235df73 | ||
|
|
430aee2b7f | ||
|
|
de2f7e4b80 | ||
|
|
6dfcc409ac | ||
|
|
78f795e1cb | ||
|
|
c69ee0f356 | ||
|
|
39475de789 | ||
|
|
bd71712765 | ||
|
|
8698686a47 | ||
|
|
762c67ee37 | ||
|
|
7bf3a29c17 | ||
|
|
6ee5002f0c | ||
|
|
fadc64add4 | ||
|
|
234f43846f | ||
|
|
67e42d9753 | ||
|
|
04f94b4bf5 | ||
|
|
d29c498457 | ||
|
|
5aa45b44b2 | ||
|
|
8273a645f2 | ||
|
|
46f292cc9b | ||
|
|
261d48059e | ||
|
|
79f931c51b | ||
|
|
399e961a65 | ||
|
|
3e61d7d146 | ||
|
|
ace95d9071 | ||
|
|
60d2a2b986 | ||
|
|
69d5661e06 | ||
|
|
01d11dfc23 | ||
|
|
d35e3ecd93 | ||
|
|
9e5a3e5385 | ||
|
|
b62163b4e8 | ||
|
|
595a00f067 | ||
|
|
a759d9fe44 | ||
|
|
7a24fd10e0 | ||
|
|
7e04a817d7 | ||
|
|
791967f45e | ||
|
|
d789436a16 | ||
|
|
96ab8ff7dd | ||
|
|
8809aca170 | ||
|
|
97106383b6 | ||
|
|
208da95234 | ||
|
|
d4c843f5f5 | ||
|
|
d273fc9f12 | ||
|
|
0b4a38218c | ||
|
|
2f57c5b5cc | ||
|
|
610f68295f | ||
|
|
39ced8eb84 | ||
|
|
368b7a1df2 | ||
|
|
8e2107cd46 | ||
|
|
fa32a33371 | ||
|
|
59116a9414 | ||
|
|
b2c732a389 | ||
|
|
0e1ffef92b | ||
|
|
a5b19d3577 | ||
|
|
185c84dd79 | ||
|
|
d554dd298c | ||
|
|
aa6ecf0aaa | ||
|
|
83692718e3 | ||
|
|
030bd95941 | ||
|
|
8722d11ac3 | ||
|
|
0131ff02c0 | ||
|
|
2d115bf1c6 | ||
|
|
946113216d | ||
|
|
77b05c6dac | ||
|
|
07c9fca210 | ||
|
|
27cf61693a | ||
|
|
37e1aa1fec | ||
|
|
31b54a3c05 | ||
|
|
589f07c103 | ||
|
|
fe8b60d873 | ||
|
|
1734619eac | ||
|
|
f5724dc9ab | ||
|
|
f7bb147b71 | ||
|
|
47fc1bc2df | ||
|
|
45172f69ba | ||
|
|
9566b138ff | ||
|
|
e15b6c2242 | ||
|
|
acc41511e0 | ||
|
|
b882b87405 | ||
|
|
518b5170ae | ||
|
|
8bfac6d9ac | ||
|
|
3ffdb4f1b2 | ||
|
|
47509450a0 | ||
|
|
812fd18aac | ||
|
|
77a13cf8ad | ||
|
|
4c914793b0 | ||
|
|
5698cfb2d3 | ||
|
|
3cd5f8f9a7 | ||
|
|
76747771de | ||
|
|
cfacd39210 | ||
|
|
90cd13f2c3 | ||
|
|
018a68246e | ||
|
|
b6a2e3ccb8 | ||
|
|
38b811f8e6 | ||
|
|
6922fa2195 | ||
|
|
4bca6cf7a5 | ||
|
|
c3484426ab | ||
|
|
0455bd366c | ||
|
|
bbe3c053f8 | ||
|
|
f47708f45d | ||
|
|
2e7a7b8cec | ||
|
|
9e067dbce9 | ||
|
|
a4a236e488 | ||
|
|
7d5b0431f5 | ||
|
|
a758c8cc6d | ||
|
|
21a64ec0f3 | ||
|
|
3f241d129a | ||
|
|
fa5f16c61f | ||
|
|
492b22506d | ||
|
|
a12f685340 | ||
|
|
2ee3f3dd66 | ||
|
|
245937e773 | ||
|
|
aef34facbd | ||
|
|
14bb5e15e3 | ||
|
|
12371f06dd | ||
|
|
28e43e968f | ||
|
|
669b413ff0 | ||
|
|
f32e5e8f9e | ||
|
|
3719378614 | ||
|
|
40c759078e | ||
|
|
e37e778e1a | ||
|
|
031607402a | ||
|
|
6c9150466c | ||
|
|
be633ebe1a | ||
|
|
9715cf922a | ||
|
|
f6163c7e19 | ||
|
|
dcc4cb19ad | ||
|
|
ad0ad12c08 | ||
|
|
74c935cdf0 | ||
|
|
1876e692f2 | ||
|
|
f198b34324 | ||
|
|
3095e0c8d9 | ||
|
|
594246127d | ||
|
|
f6c5f7a91b | ||
|
|
2a765fff7e | ||
|
|
28f958878b | ||
|
|
368f3a90eb | ||
|
|
98b14abbfc | ||
|
|
7c04c3d6b8 | ||
|
|
921cb1a115 | ||
|
|
9371968c3b | ||
|
|
47e88849ac | ||
|
|
2f0f41f413 | ||
|
|
7667765abc | ||
|
|
b2a2e69af5 | ||
|
|
7c729a72e2 | ||
|
|
5495abc881 | ||
|
|
797cd4fc06 | ||
|
|
8148d182cf | ||
|
|
7e8cab90d0 | ||
|
|
4d5743dc0b | ||
|
|
1a326cc17d | ||
|
|
cc993d8e65 | ||
|
|
f128882034 | ||
|
|
31dbb902c3 | ||
|
|
c849057aaa | ||
|
|
7b288b795c | ||
|
|
316566d479 | ||
|
|
e01ce28eb8 | ||
|
|
9b7528b61c | ||
|
|
bd22cfd34c |
13
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
name: 'Blank Issue'
|
||||
description: 请使用 https://issue.zfile.vip 创建新的问题.
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
**注意:**
|
||||
不要通过此页面创建问题, 请使用 https://issue.zfile.vip 创建新的问题.
|
||||
如果不是通过此链接创建的问题, 将会被直接关闭.
|
||||
- type: textarea
|
||||
id: add-a-description
|
||||
attributes:
|
||||
label: Add a description
|
||||
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
blank_issues_enabled: true
|
||||
contact_links:
|
||||
- name: 创建 Issue
|
||||
url: https://issue.zfile.vip/
|
||||
about: 未通过 https://issue.zfile.vip/ 创建的问题可能会被立即关闭。
|
||||
17
.gitignore
vendored
@@ -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
|
||||
@@ -21,6 +18,8 @@ mvnw.cmd
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
.fastRequest
|
||||
.murphy.yml
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
@@ -29,6 +28,12 @@ mvnw.cmd
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
build/
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
/.mvn/wrapper/
|
||||
/mvnw
|
||||
/mvnw.cmd
|
||||
/result/
|
||||
2
.package/script/log.sh
Normal file
@@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
tail -fn100 ~/.zfile-v4/logs/zfile.log
|
||||
6
.package/script/restart.sh
Normal file
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
|
||||
$DIR/stop.sh
|
||||
$DIR/start.sh
|
||||
22
.package/script/start.sh
Normal file
@@ -0,0 +1,22 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 检测是否已启动
|
||||
pid=`ps -ef | grep -n zfile | grep -v grep | grep -v launch | grep -v .sh | awk '{print $2}'`
|
||||
if [ -n "${pid}" ]
|
||||
then
|
||||
echo "已运行在 pid:${pid},无需重复启动!"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# 获取当前脚本所在路径
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
ZFILE_DIR=$(dirname "$DIR")
|
||||
|
||||
# 启动 zfile
|
||||
nohup $ZFILE_DIR/zfile/zfile --spring.config.location=$ZFILE_DIR/application.properties --spring.web.resources.static-locations=file:$ZFILE_DIR/static/ >/dev/null 2>&1 &
|
||||
echo '启动中...'
|
||||
sleep 3s
|
||||
|
||||
# 输出 pid
|
||||
pid=`ps -ef | grep -n zfile | grep -v grep | grep -v .sh | awk '{print $2}'`
|
||||
echo "目前 PID 为: ${pid}"
|
||||
12
.package/script/status.sh
Normal file
@@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "------------------ 检测状态 START --------------"
|
||||
pid=`ps -ef | grep -n zfile | grep -v grep | grep -v launch | grep -v .sh | awk '{print $2}'`
|
||||
if [ -z "${pid}" ]
|
||||
then
|
||||
echo "未运行, 无需停止!"
|
||||
else
|
||||
echo "运行pid:${pid}"
|
||||
fi
|
||||
|
||||
echo "------------------ 检测状态 END --------------"
|
||||
14
.package/script/stop.sh
Normal file
@@ -0,0 +1,14 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "------------------ 检测状态 START --------------"
|
||||
pid=`ps -ef | grep -n zfile | grep -v grep | grep -v .sh | awk '{print $2}'`
|
||||
if [ -z "${pid}" ]
|
||||
then
|
||||
echo "未运行, 无需停止!"
|
||||
else
|
||||
echo "运行pid:${pid}"
|
||||
kill -9 ${pid}
|
||||
echo "已停止进程: ${pid}"
|
||||
fi
|
||||
|
||||
echo "------------------ 检测状态 END --------------"
|
||||
BIN
.package/script/vcruntime140_1.dll
Normal file
7
.package/script/双击我启动.bat
Normal file
@@ -0,0 +1,7 @@
|
||||
@echo off
|
||||
if not exist %windir%\system32\cmd.exe (
|
||||
"%CD%\zfile\zfile.exe"
|
||||
) else (
|
||||
cmd /k "%CD%\zfile\zfile.exe"
|
||||
exit
|
||||
)
|
||||
21
Dockerfile
Normal file
@@ -0,0 +1,21 @@
|
||||
FROM debian:10-slim
|
||||
|
||||
ARG TARGETARCH
|
||||
|
||||
WORKDIR /root
|
||||
EXPOSE 8080
|
||||
|
||||
RUN apt update -y && apt install --no-install-recommends fontconfig zstd -y && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
||||
|
||||
COPY zfile-artifacts/zfile-linux-${TARGETARCH}/zfile/* /root/
|
||||
COPY zfile-artifacts/zfile-linux-${TARGETARCH}/static/ /root/static/
|
||||
COPY zfile-artifacts/zfile-linux-${TARGETARCH}/application.properties /root/
|
||||
|
||||
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
|
||||
RUN echo 'Asia/Shanghai' >/etc/timezone
|
||||
|
||||
# 设置编码为 UTF-8
|
||||
ENV LANG=C.UTF-8
|
||||
ENV LC_ALL=C.UTF-8
|
||||
|
||||
CMD if [ -f /root/zfile.zst ]; then zstd --no-progress -d /root/zfile.zst && rm -rf /root/zfile.zst && chmod +x /root/zfile && /root/zfile --spring.config.location=file:/root/application.properties; else chmod +x /root/zfile && /root/zfile --spring.config.location=file:/root/application.properties; fi
|
||||
159
README.md
@@ -1,105 +1,90 @@
|
||||
# Z-File
|
||||
|
||||

|
||||
[](https://www.codacy.com/manual/zhaojun1998/zfile?utm_source=github.com&utm_medium=referral&utm_content=zhaojun1998/zfile&utm_campaign=Badge_Grade)
|
||||

|
||||

|
||||
|
||||
此项目是一个在线文件目录的程序, 支持各种对象存储和本地存储, 使用定位是个人放常用工具下载, 或做公共的文件库. 不会向多账户方向开发.
|
||||
|
||||
前端基于 [h5ai](https://larsjung.de/h5ai/) 的原有功能使用 Vue 重新开发了一遍. 后端采用 SpringBoot, 数据库采用内嵌数据库.
|
||||
|
||||
预览地址: [http://zfile.jun6.net](http://zfile.jun6.net)
|
||||
<div align="center">
|
||||
<a href="https://zfile.vip" target="_blank" rel="noopener noreferrer">
|
||||
<img style="margin: auto; width: 100px; display: block" src="/img/logo-zfile.png" alt="ZFile" />
|
||||
</a>
|
||||
<p>ZFile 是一个适用于个人或小团队的在线网盘程序,可以将多种存储类型统一管理,再也不用登录各种网站管理文件,现在你只需要在 ZFile 中畅快使用!</p>
|
||||
<div>
|
||||
<img alt="last commit" src="https://shields.io/github/last-commit/zhaojun1998/zfile.svg?style=flat-square"/>
|
||||
<img alt="downloads" src="https://shields.io/github/downloads/zhaojun1998/zfile/total?style=flat-square"/>
|
||||
<img alt="release version" src="https://shields.io/github/v/release/zhaojun1998/zfile?style=flat-square"/>
|
||||
<img alt="commit activity" src="https://shields.io/github/commit-activity/y/zhaojun1998/zfile?style=flat-square"/>
|
||||
<img alt="open issues" src="https://shields.io/github/issues/zhaojun1998/zfile?style=flat-square"/>
|
||||
<img alt="closed issues" src="https://shields.io/github/issues-closed-raw/zhaojun1998/zfile?style=flat-square"/>
|
||||
<img alt="forks" src="https://shields.io/github/forks/zhaojun1998/zfile?style=flat-square"/>
|
||||
<img alt="stars" src="https://shields.io/github/stars/zhaojun1998/zfile?style=flat-square"/>
|
||||
<img alt="watchers" src="https://shields.io/github/watchers/zhaojun1998/zfile?style=flat-square"/>
|
||||
</div>
|
||||
<span>
|
||||
<a href="https://zfile.vip">官网</a>
|
||||
<span> | </span>
|
||||
<a href="https://docs.zfile.vip">文档</a>
|
||||
<span> | </span>
|
||||
<a href="https://demo.zfile.vip">预览地址</a>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
## 系统特色
|
||||
|
||||
* 内存缓存 (免安装)
|
||||
* 内存数据库 (免安装)
|
||||
* 个性化配置
|
||||
* 自定义目录的 header 和 footer 说明文件
|
||||
* 文件夹密码
|
||||
* 支持在线浏览文本文件, 视频, 图片, 音乐.
|
||||
* 文件/目录二维码
|
||||
* 缓存动态开启, 缓存自动刷新
|
||||
* 全局搜索
|
||||
- Docker、Docker Compose 支持(amd64, arm64)。
|
||||
- 支持对文件生成直链、短链(可设过期时间)。
|
||||
- 响应式设计,支持手机、平板、电脑等多种设备访问。
|
||||
- 支持多用户功能,可分配给指定用户指定存储源或目录。
|
||||
- 支持在线浏览图片、播放音视频,文本文件、Office、Obj(3d)等文件类型。
|
||||
- 支持对接 S3、OneDrive、SharePoint、Google Drive、多吉云、又拍云、本地存储、FTP、SFTP 等存储源。
|
||||
- 支持常用快捷键,`Ctrl + A` 全选,`Ctrl + 左键` 多选,`Shift + 左键` 范围选择,`Esc` 取消全选等。
|
||||
- 支持限速下载(捐赠版)
|
||||
- 支持限制指定用户可查看、上传的文件类型(捐赠版)
|
||||
|
||||
## 快速开始
|
||||
|
||||
安装 JDK 1.8 :
|
||||
一键脚本安装:
|
||||
|
||||
```bash
|
||||
yum instal -y java # 适用于 Centos 7.x
|
||||
curl -sSL https://docs.zfile.vip/install.sh -o install.sh && chmod +x install.sh && ./install.sh
|
||||
```
|
||||
|
||||
下载项目:
|
||||
|
||||
```bash
|
||||
wget https://github.com/zhaojun1998/zfile/releases/download/0.4/zfile-0.4.jar
|
||||
```
|
||||
|
||||
启动项目:
|
||||
|
||||
```bash
|
||||
java -Djava.security.egd=file:/dev/./urandom -jar zfile-0.4.jar
|
||||
|
||||
## 高级启动
|
||||
java -Djava.security.egd=file:/dev/./urandom -jar zfile-0.4.jar --server.port=18777
|
||||
|
||||
## 后台运行
|
||||
nohup java -Djava.security.egd=file:/dev/./urandom -jar zfile-0.4.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://docs.zfile.vip/install/)
|
||||
|
||||
|
||||
访问地址:
|
||||
## 功能预览
|
||||
|
||||
用户前台: http://127.0.0.1:8080/#/main
|
||||
### 文件列表
|
||||

|
||||
### 画廊模式
|
||||

|
||||
### 视频预览
|
||||

|
||||
### 文本预览
|
||||

|
||||
### 音频预览
|
||||

|
||||
### PDF 预览
|
||||

|
||||
### Office 预览
|
||||

|
||||
### 3d 文件预览
|
||||

|
||||
### 生成直链
|
||||

|
||||
### 页面设置
|
||||

|
||||
### 后台设置-登录
|
||||

|
||||
### 后台设置-存储源列表
|
||||

|
||||
### 后台设置-添加存储源(本地存储)
|
||||

|
||||
### 后台设置-用户管理
|
||||

|
||||
### 后台设置-显示设置
|
||||

|
||||
|
||||
初始安装: http://127.0.0.1:8080/#/install
|
||||
## 支持作者
|
||||
|
||||
管理后台: http://127.0.0.1:8080/#/admin
|
||||
如果本项目对你有帮助,请作者喝杯咖啡吧。
|
||||
|
||||
<img src="https://cdn.jun6.net/2021/03/27/152704e91f13d.png" width="400" alt="赞助我">
|
||||
|
||||
## 运行环境
|
||||
## 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 支持
|
||||
- 更方便的部署方式
|
||||
[](https://star-history.com/#zfile-dev/zfile&Date)
|
||||
|
||||
BIN
img/file-list.png
Normal file
|
After Width: | Height: | Size: 79 KiB |
BIN
img/gallery.png
Normal file
|
After Width: | Height: | Size: 563 KiB |
BIN
img/generate-link.jpeg
Normal file
|
After Width: | Height: | Size: 362 KiB |
BIN
img/login.png
Normal file
|
After Width: | Height: | Size: 59 KiB |
BIN
img/logo-zfile.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
img/page-setting.png
Normal file
|
After Width: | Height: | Size: 166 KiB |
BIN
img/preview-3d.png
Normal file
|
After Width: | Height: | Size: 213 KiB |
BIN
img/preview-audio.png
Normal file
|
After Width: | Height: | Size: 106 KiB |
BIN
img/preview-office.png
Normal file
|
After Width: | Height: | Size: 156 KiB |
BIN
img/preview-pdf.png
Normal file
|
After Width: | Height: | Size: 220 KiB |
BIN
img/preview-text.png
Normal file
|
After Width: | Height: | Size: 150 KiB |
BIN
img/preview-video.png
Normal file
|
After Width: | Height: | Size: 790 KiB |
BIN
img/storage-edit-local.png
Normal file
|
After Width: | Height: | Size: 483 KiB |
BIN
img/storage-list.png
Normal file
|
After Width: | Height: | Size: 104 KiB |
BIN
img/user-edit.png
Normal file
|
After Width: | Height: | Size: 164 KiB |
BIN
img/view-setting.png
Normal file
|
After Width: | Height: | Size: 181 KiB |
365
pom.xml
@@ -1,28 +1,61 @@
|
||||
<?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>
|
||||
|
||||
<groupId>im.zhaojun</groupId>
|
||||
<artifactId>zfile</artifactId>
|
||||
<version>4.2.0</version>
|
||||
<name>zfile</name>
|
||||
<packaging>jar</packaging>
|
||||
<description>一个在线的文件浏览系统</description>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.0.6.RELEASE</version>
|
||||
<relativePath/>
|
||||
<version>3.3.2</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
|
||||
<groupId>im.zhaojun</groupId>
|
||||
<artifactId>zfile</artifactId>
|
||||
<version>0.4</version>
|
||||
<name>zfile</name>
|
||||
<description>一个在线的文件浏览系统</description>
|
||||
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
<skipTests>true</skipTests>
|
||||
|
||||
<java.version>21</java.version>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
<maven.compiler.target>21</maven.compiler.target>
|
||||
|
||||
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
|
||||
<org.mapstruct.version>1.5.3.Final</org.mapstruct.version>
|
||||
<snakeyaml.version>2.0</snakeyaml.version>
|
||||
<jackson-bom.version>2.14.1</jackson-bom.version>
|
||||
<sqlite-jdbc.version>3.46.0.1</sqlite-jdbc.version>
|
||||
<flyway.version>10.12.0</flyway.version>
|
||||
|
||||
<lombok.version>1.18.32</lombok.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>software.amazon.awssdk</groupId>
|
||||
<artifactId>bom</artifactId>
|
||||
<version>2.24.3</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<!-- spring boot 官方相关 -->
|
||||
<dependencies>
|
||||
<!-- spring boot 官方相关-->
|
||||
<dependency>
|
||||
<groupId>org.graalvm.sdk</groupId>
|
||||
<artifactId>graal-sdk</artifactId>
|
||||
<version>24.1.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
@@ -31,63 +64,140 @@
|
||||
<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>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<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>com.mysql</groupId>
|
||||
<artifactId>mysql-connector-j</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<scope>runtime</scope>
|
||||
<groupId>org.xerial</groupId>
|
||||
<artifactId>sqlite-jdbc</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 工具类 -->
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>4.5.11</version>
|
||||
<groupId>org.flywaydb</groupId>
|
||||
<artifactId>flyway-core</artifactId>
|
||||
<version>${flyway.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.flywaydb</groupId>
|
||||
<artifactId>flyway-mysql</artifactId>
|
||||
<version>${flyway.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 存储引擎相关 API, 对象存储、FTP、 Rest API-->
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
|
||||
<version>3.5.6</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- 存储策略相关 API, 对象存储、FTP、 Rest API-->
|
||||
<dependency>
|
||||
<groupId>com.upyun</groupId>
|
||||
<artifactId>java-sdk</artifactId>
|
||||
<version>4.1.3</version>
|
||||
<version>4.2.3</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/software.amazon.awssdk/s3 -->
|
||||
<dependency>
|
||||
<groupId>software.amazon.awssdk</groupId>
|
||||
<artifactId>s3</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.amazonaws</groupId>
|
||||
<artifactId>aws-java-sdk-s3</artifactId>
|
||||
<version>1.11.699</version>
|
||||
<groupId>com.qiniu</groupId>
|
||||
<artifactId>qiniu-java-sdk</artifactId>
|
||||
<version>7.12.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
<version>4.5.8</version>
|
||||
<groupId>com.github.mwiede</groupId>
|
||||
<artifactId>jsch</artifactId>
|
||||
<version>0.2.20</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-net</groupId>
|
||||
<artifactId>commons-net</artifactId>
|
||||
<version>3.6</version>
|
||||
<groupId>com.github.lookfirst</groupId>
|
||||
<artifactId>sardine</artifactId>
|
||||
<version>5.12</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-simple</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.api-client</groupId>
|
||||
<artifactId>google-api-client</artifactId>
|
||||
<version>1.35.2</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 登陆/权限相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-boot3-starter</artifactId>
|
||||
<version>1.38.0</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- 文档相关 -->
|
||||
<dependency>
|
||||
<groupId>com.github.xiaoymin</groupId>
|
||||
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
|
||||
<version>4.5.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 工具类 -->
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>com.hierynomus</groupId>-->
|
||||
<!-- <artifactId>sshj</artifactId>-->
|
||||
<!-- <version>0.38.0</version>-->
|
||||
<!-- </dependency>-->
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>5.8.28</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.poi</groupId>
|
||||
<artifactId>poi-ooxml</artifactId>
|
||||
<version>5.2.5</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-compress</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-compress</artifactId>
|
||||
<version>1.26.2</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- 其他工具类 -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
@@ -95,15 +205,89 @@
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.mpatric</groupId>
|
||||
<artifactId>mp3agic</artifactId>
|
||||
<version>0.9.1</version>
|
||||
<groupId>commons-net</groupId>
|
||||
<artifactId>commons-net</artifactId>
|
||||
<version>3.11.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-pool2</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.fastjson2</groupId>
|
||||
<artifactId>fastjson2</artifactId>
|
||||
<version>2.0.29</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>33.2.0-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</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>20231013</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpmime</artifactId>
|
||||
<version>4.5.13</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents.client5</groupId>
|
||||
<artifactId>httpclient5</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk15on</artifactId>
|
||||
<version>1.70</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.retry</groupId>
|
||||
<artifactId>spring-retry</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
|
||||
<dependency>
|
||||
<groupId>commons-fileupload</groupId>
|
||||
<artifactId>commons-fileupload</artifactId>
|
||||
<version>1.5</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alicp.jetcache</groupId>
|
||||
<artifactId>jetcache-starter-redis</artifactId>
|
||||
<version>2.5.14</version>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>dns-cache-manipulator</artifactId>
|
||||
<version>1.8.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.oshi</groupId>
|
||||
<artifactId>oshi-core</artifactId>
|
||||
<version>6.6.3</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
@@ -114,7 +298,82 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>21</source>
|
||||
<target>21</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.32</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>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>native</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.graalvm.buildtools</groupId>
|
||||
<artifactId>native-maven-plugin</artifactId>
|
||||
<extensions>true</extensions>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>build-native</id>
|
||||
<goals>
|
||||
<goal>compile-no-fork</goal>
|
||||
</goals>
|
||||
<phase>package</phase>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<fallback>false</fallback>
|
||||
<imageName>${project.name}</imageName>
|
||||
<metadataRepository>
|
||||
<enabled>true</enabled>
|
||||
</metadataRepository>
|
||||
<jvmArgs>
|
||||
<jvmArg>--add-opens=java.base/java.net=ALL-UNNAMED</jvmArg>
|
||||
<jvmArg>--add-opens=java.base/sun.net=ALL-UNNAMED</jvmArg>
|
||||
</jvmArgs>
|
||||
<buildArgs>
|
||||
<arg>
|
||||
-march=compatibility
|
||||
-H:+AddAllCharsets
|
||||
--features=im.zhaojun.zfile.aot.LambdaRegistrationFeature
|
||||
--features=im.zhaojun.zfile.aot.BouncyCastleFeature
|
||||
--features=im.zhaojun.zfile.aot.SQLiteNativeConfiguration
|
||||
</arg>
|
||||
</buildArgs>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
</project>
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,61 +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;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @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();
|
||||
|
||||
if (Objects.isNull(accessKey) || Objects.isNull(secretKey) || Objects.isNull(endPoint) || Objects.isNull(bucketName)) {
|
||||
isInitialized = false;
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,31 +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) {
|
||||
throw new StorageStrategyUninitializedException("存储策略尚未初始化, 请联系管理员!");
|
||||
}
|
||||
if (currentFileService.getIsUnInitialized()) {
|
||||
throw new StorageStrategyUninitializedException("存储策略异常, 请联系管理员!");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
package im.zhaojun.common.config;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.web.cors.CorsUtils;
|
||||
import org.springframework.web.filter.GenericFilterBean;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 开启跨域支持. 一般用于开发环境, 或前后端分离部署时开启.
|
||||
* @author zhaojun
|
||||
*/
|
||||
public class CorsFilter extends GenericFilterBean {
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
|
||||
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
|
||||
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
|
||||
|
||||
// Set customized header
|
||||
httpServletResponse.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, httpServletRequest.getHeader(HttpHeaders.ORIGIN));
|
||||
httpServletResponse.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, "Origin, X-Requested-With, Content-Type, Accept");
|
||||
httpServletResponse.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, "GET, POST, PUT, DELETE, OPTIONS");
|
||||
httpServletResponse.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
|
||||
httpServletResponse.setHeader(HttpHeaders.ACCESS_CONTROL_MAX_AGE, "3600");
|
||||
|
||||
if (!CorsUtils.isPreFlightRequest(httpServletRequest)) {
|
||||
chain.doFilter(httpServletRequest, httpServletResponse);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,147 +0,0 @@
|
||||
package im.zhaojun.common.controller;
|
||||
|
||||
import im.zhaojun.common.model.StorageConfig;
|
||||
import im.zhaojun.common.model.dto.CacheConfigDTO;
|
||||
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;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 后台管理
|
||||
* @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("/config")
|
||||
public ResultBean updateConfig(SystemConfigDTO systemConfigDTO) throws Exception {
|
||||
StorageTypeEnum currentStorageStrategy = systemConfigService.getCurrentStorageStrategy();
|
||||
|
||||
systemConfigDTO.setId(1);
|
||||
systemConfigService.updateSystemConfig(systemConfigDTO);
|
||||
|
||||
if (!currentStorageStrategy.equals(systemConfigDTO.getStorageStrategy())) {
|
||||
refreshStorageStrategy();
|
||||
}
|
||||
|
||||
return ResultBean.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改管理员登陆密码
|
||||
*/
|
||||
@PostMapping("/update-pwd")
|
||||
public ResultBean updatePwd(String username, String password) {
|
||||
systemConfigService.updateUsernameAndPwd(username, password);
|
||||
return ResultBean.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定存储引擎的设置
|
||||
* @param storageType 存储引擎
|
||||
* @return 所有设置
|
||||
*/
|
||||
@GetMapping("/strategy-form")
|
||||
public ResultBean getFormByStorageType(StorageTypeEnum storageType) {
|
||||
List<StorageConfig> storageConfigList = storageConfigService.selectStorageConfigByType(storageType);
|
||||
return ResultBean.success(storageConfigList);
|
||||
}
|
||||
|
||||
@GetMapping("/cache/config")
|
||||
public ResultBean cacheConfig() throws Exception {
|
||||
AbstractFileService fileService = systemConfigService.getCurrentFileService();
|
||||
Set<String> cacheKeys = fileService.getCacheKeys();
|
||||
|
||||
CacheConfigDTO cacheConfigDTO = new CacheConfigDTO();
|
||||
cacheConfigDTO.setEnableCache(systemConfigService.getEnableCache());
|
||||
cacheConfigDTO.setCacheFinish(fileAsyncCacheService.isCacheFinish());
|
||||
cacheConfigDTO.setCacheKeys(cacheKeys);
|
||||
|
||||
return ResultBean.success(cacheConfigDTO);
|
||||
}
|
||||
|
||||
@PostMapping("/cache/refresh")
|
||||
public ResultBean refreshCache(String key) throws Exception {
|
||||
AbstractFileService fileService = systemConfigService.getCurrentFileService();
|
||||
fileService.refreshCache(key);
|
||||
return ResultBean.success();
|
||||
}
|
||||
|
||||
@PostMapping("/cache/clear")
|
||||
public ResultBean clearCache(String key) throws Exception {
|
||||
AbstractFileService fileService = systemConfigService.getCurrentFileService();
|
||||
fileService.clearCache();
|
||||
return ResultBean.success();
|
||||
}
|
||||
|
||||
@PostMapping("/cache/all")
|
||||
public ResultBean cacheAll() throws Exception {
|
||||
AbstractFileService fileService = systemConfigService.getCurrentFileService();
|
||||
fileService.clearCache();
|
||||
fileAsyncCacheService.cacheGlobalFile();
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,167 +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.FileAsyncCacheService;
|
||||
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;
|
||||
|
||||
@Resource
|
||||
private FileAsyncCacheService fileAsyncCacheService;
|
||||
|
||||
/**
|
||||
* 滚动加载每页条数.
|
||||
*/
|
||||
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 = new ArrayList<>(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() throws Exception {
|
||||
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("搜索功能未开启");
|
||||
}
|
||||
if (!fileAsyncCacheService.isCacheFinish()) {
|
||||
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()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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) throws Exception {
|
||||
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) throws Exception {
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -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("系统异常, 请联系管理员");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
|
||||
}
|
||||
@@ -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";
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package im.zhaojun.common.model.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
* @date 2020/1/3 12:39
|
||||
*/
|
||||
@Data
|
||||
public class CacheConfigDTO {
|
||||
private boolean enableCache;
|
||||
private boolean cacheFinish;
|
||||
private Set<String> cacheKeys;
|
||||
}
|
||||
@@ -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 + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -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 +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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 +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -1,182 +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 javax.annotation.Resource;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
* @date 2019/12/28 19:27
|
||||
*/
|
||||
@Slf4j
|
||||
public abstract class AbstractFileService implements FileService {
|
||||
|
||||
public static final String SYSTEM_CONFIG_CACHE_PREFIX = "zfile-cache:";
|
||||
|
||||
@Value("${zfile.cache.timeout}")
|
||||
protected Long timeout;
|
||||
|
||||
protected boolean isInitialized;
|
||||
|
||||
@Resource
|
||||
private SystemConfigService systemConfigService;
|
||||
|
||||
@Resource
|
||||
private FileAsyncCacheService fileAsyncCacheService;
|
||||
|
||||
@CreateCache(name = SYSTEM_CONFIG_CACHE_PREFIX, cacheType = CacheType.LOCAL)
|
||||
private Cache<String, List<FileItemDTO>> cache;
|
||||
|
||||
/***
|
||||
* 获取指定路径下的文件及文件夹, 默认缓存 60 分钟,每隔 30 分钟刷新一次.
|
||||
* @param path 文件路径
|
||||
* @return 文件及文件夹列表
|
||||
* @throws Exception 获取文件列表中出现的异常
|
||||
*/
|
||||
@Override
|
||||
@Cached(name = SYSTEM_CONFIG_CACHE_PREFIX,
|
||||
key = "args[0]",
|
||||
cacheType = CacheType.LOCAL, localLimit = 100000, condition = "mvel{bean('systemConfigService').enableCache}")
|
||||
@CacheRefresh(refresh = 30, timeUnit = TimeUnit.MINUTES)
|
||||
public abstract List<FileItemDTO> fileList(String path) throws Exception;
|
||||
|
||||
/**
|
||||
* 清理当前存储引擎的缓存
|
||||
*/
|
||||
public void clearCache() throws Exception {
|
||||
Set<String> cacheKeys = getCacheKeys();
|
||||
cache.removeAll(cacheKeys);
|
||||
fileAsyncCacheService.setCacheFinish(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化方法, 启动时自动调用实现类的此方法进行初始化.
|
||||
*/
|
||||
@PostConstruct
|
||||
public abstract void init();
|
||||
|
||||
protected boolean testConnection() {
|
||||
boolean flag = true;
|
||||
try {
|
||||
fileList("/");
|
||||
} catch (Exception e) {
|
||||
log.debug(getStorageTypeEnum().getDescription() + "初始化异常", e);
|
||||
flag = false;
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取是否初始化成功
|
||||
* @return 初始化成功与否
|
||||
*/
|
||||
public boolean getIsUnInitialized() {
|
||||
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 所有文件
|
||||
*/
|
||||
public List<FileItemDTO> selectAllFileList() throws Exception {
|
||||
List<FileItemDTO> result = new ArrayList<>();
|
||||
boolean enableCache = systemConfigService.getEnableCache();
|
||||
if (!enableCache) {
|
||||
log.debug("未开启缓存, 不支持查询所有文件.");
|
||||
return null;
|
||||
}
|
||||
String path = "/";
|
||||
|
||||
List<FileItemDTO> fileItemList = cache.get(path);
|
||||
fileItemList = fileItemList == null ? new ArrayList<>() : fileItemList;
|
||||
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() + "/");
|
||||
List<FileItemDTO> cacheList = cache.get(filePath);
|
||||
if (cacheList != null) {
|
||||
queue.addAll(cacheList);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有缓存的 Key, 仅当开启缓存, 且缓存完成时, 可获取.
|
||||
* @return 所有缓存的 Key
|
||||
* @throws Exception 可能出现的异常
|
||||
*/
|
||||
public Set<String> getCacheKeys() throws Exception {
|
||||
if (systemConfigService.getEnableCache() && fileAsyncCacheService.isCacheFinish()) {
|
||||
Set<String> collect = selectAllFileList().stream().map(fileItemDTO -> {
|
||||
if (fileItemDTO.getType() == FileTypeEnum.FOLDER) {
|
||||
return StringUtils.removeDuplicateSeparator("/" + fileItemDTO.getPath() + "/" + fileItemDTO.getName() + "/");
|
||||
}
|
||||
return null;
|
||||
}).collect(Collectors.toSet());
|
||||
collect.remove(null);
|
||||
return collect;
|
||||
} else {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新缓存
|
||||
*/
|
||||
public void refreshCache(String key) throws Exception {
|
||||
cache.remove(key);
|
||||
FileService currentFileService = (FileService) AopContext.currentProxy();
|
||||
currentFileService.fileList(key);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
package im.zhaojun.common.service;
|
||||
|
||||
import im.zhaojun.common.config.StorageTypeFactory;
|
||||
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.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class FileAsyncCacheService {
|
||||
|
||||
private boolean cacheFinish;
|
||||
|
||||
@Resource
|
||||
private SystemConfigService systemConfigService;
|
||||
|
||||
@Async
|
||||
public void cacheGlobalFile() {
|
||||
StorageTypeEnum storageStrategy = systemConfigService.getCurrentStorageStrategy();
|
||||
|
||||
if (storageStrategy == null) {
|
||||
log.info("尚未配置存储策略. 跳过启动缓存.");
|
||||
return;
|
||||
}
|
||||
|
||||
boolean enableCache = systemConfigService.getEnableCache();
|
||||
if (!enableCache) {
|
||||
log.info("未开启缓存, 跳过启动缓存");
|
||||
return;
|
||||
}
|
||||
|
||||
AbstractFileService fileService = StorageTypeFactory.getStorageTypeService(storageStrategy);
|
||||
|
||||
|
||||
if (fileService.getIsUnInitialized()) {
|
||||
log.info("存储引擎 {} 未初始化成功, 跳过启动缓存.", storageStrategy.getDescription());
|
||||
return;
|
||||
}
|
||||
|
||||
log.info("缓存 {} 所有文件开始", storageStrategy.getDescription());
|
||||
long startTime = System.currentTimeMillis();
|
||||
try {
|
||||
String path = "/";
|
||||
|
||||
FileService currentFileService = systemConfigService.getCurrentFileService();
|
||||
List<FileItemDTO> fileItemList = currentFileService.fileList(path);
|
||||
ArrayDeque<FileItemDTO> queue = new ArrayDeque<>(fileItemList);
|
||||
|
||||
while (!queue.isEmpty()) {
|
||||
FileItemDTO fileItemDTO = queue.pop();
|
||||
if (fileItemDTO.getType() == FileTypeEnum.FOLDER) {
|
||||
String filePath = StringUtils.removeDuplicateSeparator("/" + fileItemDTO.getPath() + "/" + fileItemDTO.getName() + "/");
|
||||
queue.addAll(currentFileService.fileList(filePath));
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("缓存所有文件失败", e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
long endTime = System.currentTimeMillis();
|
||||
log.info("缓存 {} 所有文件结束, 用时: {} 秒", storageStrategy.getDescription(), ( (endTime - startTime) / 1000 ));
|
||||
cacheFinish = true;
|
||||
}
|
||||
|
||||
|
||||
public boolean isCacheFinish() {
|
||||
return cacheFinish;
|
||||
}
|
||||
|
||||
public void setCacheFinish(boolean cacheFinish) {
|
||||
this.cacheFinish = cacheFinish;
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
/***
|
||||
* 获取指定路径下的文件及文件夹
|
||||
* @param path 文件路径
|
||||
* @return 文件及文件夹列表
|
||||
* @throws Exception 获取文件列表中出现的异常
|
||||
*/
|
||||
List<FileItemDTO> fileList(String path) throws Exception;
|
||||
|
||||
/**
|
||||
* 获取文件下载地址
|
||||
* @param path 文件路径
|
||||
* @return 文件下载地址
|
||||
*/
|
||||
String getDownloadUrl(String path);
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,174 +0,0 @@
|
||||
package im.zhaojun.common.service;
|
||||
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import com.alicp.jetcache.Cache;
|
||||
import com.alicp.jetcache.anno.CacheType;
|
||||
import com.alicp.jetcache.anno.CreateCache;
|
||||
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 lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class SystemConfigService {
|
||||
|
||||
public static final String SYSTEM_CONFIG_CACHE_PREFIX = "zfile-config-cache:";
|
||||
|
||||
public static final String SYSTEM_CONFIG_CACHE_KEY = "1";
|
||||
|
||||
@CreateCache(name = SYSTEM_CONFIG_CACHE_PREFIX, cacheType = CacheType.LOCAL)
|
||||
private Cache<String, Object> configCache;
|
||||
|
||||
@Resource
|
||||
private SystemConfigRepository systemConfigRepository;
|
||||
|
||||
@Resource
|
||||
private FileAsyncCacheService fileAsyncCacheService;
|
||||
|
||||
public SystemConfigDTO getSystemConfig() {
|
||||
Object cache = configCache.get(SYSTEM_CONFIG_CACHE_KEY);
|
||||
if (configCache.get(SYSTEM_CONFIG_CACHE_KEY) != null) {
|
||||
return (SystemConfigDTO) cache;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
configCache.put(SYSTEM_CONFIG_CACHE_KEY, systemConfigDTO);
|
||||
return systemConfigDTO;
|
||||
}
|
||||
|
||||
public void updateSystemConfig(SystemConfigDTO systemConfigDTO) throws Exception {
|
||||
|
||||
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);
|
||||
|
||||
boolean oldEnableCache = getEnableCache();
|
||||
Boolean curEnableCache = systemConfigDTO.getEnableCache();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
configCache.remove(SYSTEM_CONFIG_CACHE_KEY);
|
||||
|
||||
systemConfigRepository.saveAll(systemConfigList);
|
||||
|
||||
if (!oldEnableCache && curEnableCache) {
|
||||
log.debug("检测到开启了缓存, 开启预热缓存");
|
||||
fileAsyncCacheService.cacheGlobalFile();
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
configCache.remove(SYSTEM_CONFIG_CACHE_KEY);
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,42 +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.ArrayList;
|
||||
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 = new ArrayList<>(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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -1,93 +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.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @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();
|
||||
|
||||
if (Objects.isNull(host) || Objects.isNull(port) || Objects.isNull(username) || Objects.isNull(password)) {
|
||||
isInitialized = true;
|
||||
} else {
|
||||
ftp = new Ftp(host, Integer.parseInt(port), username, password);
|
||||
isInitialized = testConnection();
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.debug(getStorageTypeEnum().getDescription() + "初始化异常, 已跳过");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
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
|
||||
public String getDownloadUrl(String path) {
|
||||
return URLUtil.complateUrl(domain, path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StorageTypeEnum getStorageTypeEnum() {
|
||||
return StorageTypeEnum.FTP;
|
||||
}
|
||||
}
|
||||
@@ -1,59 +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;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @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();
|
||||
|
||||
if (Objects.isNull(accessKey) || Objects.isNull(secretKey) || Objects.isNull(endPoint) || Objects.isNull(bucketName)) {
|
||||
isInitialized = false;
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,106 +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;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @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();
|
||||
if (Objects.isNull(filePath)) {
|
||||
isInitialized = false;
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,59 +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;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @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();
|
||||
|
||||
if (Objects.isNull(accessKey) || Objects.isNull(secretKey) || Objects.isNull(endPoint) || Objects.isNull(bucketName)) {
|
||||
isInitialized = false;
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
// }
|
||||
// }
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,59 +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.stereotype.Service;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @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();
|
||||
|
||||
if (Objects.isNull(accessKey) || Objects.isNull(secretKey) || Objects.isNull(endPoint) || Objects.isNull(bucketName)) {
|
||||
isInitialized = false;
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,60 +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;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @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 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();
|
||||
|
||||
if (Objects.isNull(secretId) || Objects.isNull(secretKey) || Objects.isNull(endPoint) || Objects.isNull(bucketName)) {
|
||||
isInitialized = false;
|
||||
} else {
|
||||
BasicAWSCredentials credentials = new BasicAWSCredentials(secretId, secretKey);
|
||||
s3Client = AmazonS3ClientBuilder.standard()
|
||||
.withCredentials(new AWSStaticCredentialsProvider(credentials))
|
||||
.withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(endPoint, "cos")).build();
|
||||
|
||||
isInitialized = testConnection();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.debug(getStorageTypeEnum().getDescription() + "初始化异常, 已跳过");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public StorageTypeEnum getStorageTypeEnum() {
|
||||
return StorageTypeEnum.TENCENT;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
package im.zhaojun.upyun.service;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
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.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @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();
|
||||
basePath = ObjectUtil.defaultIfNull(basePath, "");
|
||||
|
||||
if (Objects.isNull(bucketName) || Objects.isNull(username) || Objects.isNull(password)) {
|
||||
isInitialized = false;
|
||||
} else {
|
||||
upYun = new UpYun(bucketName, username, password);
|
||||
isInitialized = testConnection();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.debug(getStorageTypeEnum().getDescription() + "初始化异常, 已跳过");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
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
|
||||
public String getDownloadUrl(String path) {
|
||||
return URLUtil.complateUrl(domain, path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StorageTypeEnum getStorageTypeEnum() {
|
||||
return StorageTypeEnum.UPYUN;
|
||||
}
|
||||
|
||||
}
|
||||
23
src/main/java/im/zhaojun/zfile/ZfileApplication.java
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
33
src/main/java/im/zhaojun/zfile/core/annotation/ApiLimit.java
Normal file
@@ -0,0 +1,33 @@
|
||||
package im.zhaojun.zfile.core.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 接口限流注解
|
||||
*
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface ApiLimit {
|
||||
|
||||
/**
|
||||
* 持续时间
|
||||
*/
|
||||
int timeout();
|
||||
|
||||
/**
|
||||
* 时间单位, 默认为秒
|
||||
*/
|
||||
TimeUnit timeUnit() default TimeUnit.SECONDS;
|
||||
|
||||
/**
|
||||
* 单位时间内允许访问的最大次数
|
||||
*/
|
||||
long maxCount();
|
||||
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package im.zhaojun.common.annotation;
|
||||
package im.zhaojun.zfile.core.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
@@ -6,10 +6,12 @@ import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 标记注解, 用于在调用前检查是否已存储策略
|
||||
* 演示系统禁用功能注解
|
||||
*
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface CheckStorageStrategyInit {
|
||||
}
|
||||
public @interface DemoDisable {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package im.zhaojun.zfile.core.aspect;
|
||||
|
||||
import cn.hutool.cache.CacheUtil;
|
||||
import cn.hutool.cache.impl.TimedCache;
|
||||
import cn.hutool.extra.servlet.JakartaServletUtil;
|
||||
import im.zhaojun.zfile.core.annotation.ApiLimit;
|
||||
import im.zhaojun.zfile.core.exception.ErrorCode;
|
||||
import im.zhaojun.zfile.core.exception.core.BizException;
|
||||
import im.zhaojun.zfile.core.util.RequestHolder;
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Before;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
* 接口限流切面, 通过注解 {@link ApiLimit} 进行限流.
|
||||
*
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Aspect
|
||||
@Component
|
||||
public class ApiLimitAspect {
|
||||
|
||||
private final TimedCache<String, AtomicLong> apiLimitTimedCache = CacheUtil.newTimedCache(1000);
|
||||
|
||||
public static final String API_LIMIT_KEY_PREFIX = "api_limit_";
|
||||
|
||||
/**
|
||||
* 定义一个切点(通过注解)
|
||||
*/
|
||||
@Pointcut("@annotation(im.zhaojun.zfile.core.annotation.ApiLimit)")
|
||||
public void apiLimit() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 在标记了 {@link ApiLimit} 注解的方法执行前进行限流校验.
|
||||
*
|
||||
* @param joinPoint 切点
|
||||
*/
|
||||
@Before("apiLimit()")
|
||||
public void before(JoinPoint joinPoint) {
|
||||
// 获取当前请求的方法上的注解中设置的值
|
||||
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
|
||||
// 反射获取当前被调用的方法
|
||||
Method method = signature.getMethod();
|
||||
// 获取方法中的注解
|
||||
ApiLimit apiLimit = method.getDeclaredAnnotation(ApiLimit.class);
|
||||
int timeout = apiLimit.timeout();
|
||||
TimeUnit timeUnit = apiLimit.timeUnit();
|
||||
long millis = timeUnit.toMillis(timeout);
|
||||
long maxCount = apiLimit.maxCount();
|
||||
|
||||
// 获取请求相关信息
|
||||
String ip = JakartaServletUtil.getClientIP(RequestHolder.getRequest());
|
||||
|
||||
// 限制访问次数
|
||||
String key = API_LIMIT_KEY_PREFIX.concat(ip).concat(method.getName());
|
||||
AtomicLong atomicLong = apiLimitTimedCache.get(key, false);
|
||||
if (atomicLong == null) {
|
||||
apiLimitTimedCache.put(key, new AtomicLong(1), millis);
|
||||
} else {
|
||||
if (atomicLong.incrementAndGet() > maxCount) {
|
||||
throw new BizException(ErrorCode.BIZ_ACCESS_TOO_FREQUENT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package im.zhaojun.zfile.core.aspect;
|
||||
|
||||
import im.zhaojun.zfile.core.constant.MdcConstant;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Controller 切面, 用于处理返回值统一封装.
|
||||
*
|
||||
* @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<?> baseResponse) {
|
||||
// 将 MDC 中的 TraceId 设置到返回值中
|
||||
baseResponse.setTraceId(MDC.get(MdcConstant.TRACE_ID));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package im.zhaojun.zfile.core.aspect;
|
||||
|
||||
import im.zhaojun.zfile.core.annotation.DemoDisable;
|
||||
import im.zhaojun.zfile.core.config.ZFileProperties;
|
||||
import im.zhaojun.zfile.core.exception.ErrorCode;
|
||||
import im.zhaojun.zfile.core.exception.core.BizException;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Before;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 通过注解 {@link DemoDisable} 限制演示系统不可操作的功能.
|
||||
*
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Aspect
|
||||
@Component
|
||||
public class DemoDisableAspect {
|
||||
|
||||
@Resource
|
||||
private ZFileProperties zFileProperties;
|
||||
|
||||
/**
|
||||
* 定义一个切点(通过注解)
|
||||
*/
|
||||
@Pointcut("@annotation(im.zhaojun.zfile.core.annotation.DemoDisable)")
|
||||
public void demoDisable() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 在标记了 {@link DemoDisable} 注解的方法执行前进行限流校验.
|
||||
*
|
||||
* @param joinPoint 切点
|
||||
*/
|
||||
@Before("demoDisable()")
|
||||
public void before(JoinPoint joinPoint) {
|
||||
if (zFileProperties.isDemoSite()) {
|
||||
throw new BizException(ErrorCode.DEMO_SITE_DISABLE_OPERATOR);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
* ZFile 配置类,将配置文件中的 zfile 配置项映射到该类中.
|
||||
*
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Data
|
||||
@EnableConfigurationProperties
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "zfile")
|
||||
public class ZFileProperties {
|
||||
|
||||
private boolean debug;
|
||||
|
||||
private String version;
|
||||
|
||||
private boolean isDemoSite;
|
||||
|
||||
private OAuth2Properties onedrive = new OAuth2Properties();
|
||||
private OAuth2Properties onedriveChina = new OAuth2Properties();
|
||||
private OAuth2Properties gd = new OAuth2Properties();
|
||||
|
||||
@Data
|
||||
public static class OAuth2Properties {
|
||||
private String clientId;
|
||||
private String clientSecret;
|
||||
private String redirectUri;
|
||||
private String scope;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
package im.zhaojun.zfile.core.config.datasource;
|
||||
|
||||
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
import im.zhaojun.zfile.core.util.StringUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.boot.autoconfigure.flyway.FlywayProperties;
|
||||
import org.springframework.core.PriorityOrdered;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 在 Spring 容器初始化时, 对数据源进行处理.
|
||||
* <br/>
|
||||
* 1. 针对 DataSource 进行处理,仅针对 sqlite:
|
||||
* <ul>
|
||||
* <li>提前创建 sqlite 数据文件所在目录.</li>
|
||||
* <li>检测到版本更新时(pom.xml -> project.version)自动备份原数据库.</li>
|
||||
* </ul>
|
||||
* <br/>
|
||||
* 2. 针对 Flyway 进行处理,根据数据库类型, 配置不同的 Flyway Migration Location:
|
||||
* <ul>
|
||||
* <li>SQLite 数据库使用 migration-sqlite 目录.</li>
|
||||
* <li>MySQL 数据库使用 migration-mysql 目录.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class DataSourceBeanPostProcessor implements BeanPostProcessor, PriorityOrdered {
|
||||
|
||||
public static final String ZFILE_VERSION_PROPERTIES = "zfile.version";
|
||||
|
||||
public static final String DRIVE_CLASS_NAME_PROPERTIES = "spring.datasource.driver-class-name";
|
||||
|
||||
public static final String DATA_SOURCE_BEAN_NAME = "dataSource";
|
||||
|
||||
public static final String SQLITE_DRIVE_CLASS_NAME = "org.sqlite.JDBC";
|
||||
|
||||
public static final String MYSQL_DRIVE_CLASS_NAME = "com.mysql.cj.jdbc.Driver";
|
||||
|
||||
@Override
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
|
||||
// 如果更改了数据源类型这里要修改
|
||||
if (bean instanceof HikariDataSource dataSource && DATA_SOURCE_BEAN_NAME.equals(beanName)) {
|
||||
processSqliteDataSource(dataSource);
|
||||
} else if (bean instanceof FlywayProperties flywayProperties) {
|
||||
processFlywayLocations(flywayProperties);
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果是 sqlite 数据库, 提前创建数据库文件所在目录. <br/>
|
||||
*
|
||||
* 如果检测到版本更新, 自动备份原数据库文件.
|
||||
*
|
||||
* @param dataSource
|
||||
* 数据源
|
||||
*/
|
||||
private void processSqliteDataSource(HikariDataSource dataSource) {
|
||||
String driverClassName = dataSource.getDriverClassName();
|
||||
String jdbcUrl = dataSource.getJdbcUrl();
|
||||
if (StringUtils.equals(driverClassName, SQLITE_DRIVE_CLASS_NAME)) {
|
||||
String path = jdbcUrl.replace("jdbc:sqlite:", "");
|
||||
String folderPath = FileUtil.getAbsolutePath(new File(path).getParentFile());
|
||||
log.info("SQLite 数据库文件所在目录: [{}]", folderPath);
|
||||
File file = new File(folderPath);
|
||||
if (!file.exists()) {
|
||||
log.info("检测到 SQLite 数据库文件所在目录不存在, 已自动创建.");
|
||||
if (!file.mkdirs()) {
|
||||
log.error("SQLite 数据库文件创建失败.");
|
||||
}
|
||||
} else {
|
||||
log.info("检测到 SQLite 数据库文件所在目录已存在, 无需自动创建.");
|
||||
|
||||
// 更新版本时, 先自动备份数据库文件
|
||||
String version = SpringUtil.getProperty(ZFILE_VERSION_PROPERTIES);
|
||||
if (StringUtils.isNotEmpty(version)) {
|
||||
String backupPath = folderPath + "/zfile-update-" + version + "-backup.db";
|
||||
if (!FileUtil.exist(path)) {
|
||||
log.error("检测到 SQLite 数据库文件不存在, 一般为初始化状态,无需备份.");
|
||||
return;
|
||||
}
|
||||
if (FileUtil.exist(backupPath)) {
|
||||
log.info("检测到 SQLite 数据库备份文件 [{}] 已存在, 无需再次备份.", backupPath);
|
||||
} else {
|
||||
FileUtil.copy(path, backupPath, false);
|
||||
log.info("自动备份 SQLite 数据库文件到: [{}]", backupPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据使用的不同数据库, 配置使用不同的 migration location
|
||||
*
|
||||
* @param flywayProperties
|
||||
* flyway 配置项
|
||||
*/
|
||||
private void processFlywayLocations(FlywayProperties flywayProperties) {
|
||||
String driveClassName = SpringUtil.getProperty(DRIVE_CLASS_NAME_PROPERTIES);
|
||||
if (SQLITE_DRIVE_CLASS_NAME.equals(driveClassName)) {
|
||||
flywayProperties.setLocations(List.of("classpath:db/migration-sqlite"));
|
||||
} else if (MYSQL_DRIVE_CLASS_NAME.equals(driveClassName)) {
|
||||
flywayProperties.setLocations(List.of("classpath:db/migration-mysql"));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return Integer.MIN_VALUE;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package im.zhaojun.zfile.core.config.docs;
|
||||
|
||||
import io.swagger.v3.oas.models.OpenAPI;
|
||||
import io.swagger.v3.oas.models.info.Contact;
|
||||
import io.swagger.v3.oas.models.info.Info;
|
||||
import io.swagger.v3.oas.models.info.License;
|
||||
import io.swagger.v3.oas.models.media.StringSchema;
|
||||
import io.swagger.v3.oas.models.parameters.HeaderParameter;
|
||||
import org.springdoc.core.customizers.OperationCustomizer;
|
||||
import org.springdoc.core.models.GroupedOpenApi;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Knife4j 参数配置,区分前台功能和管理员功能,并为管理员接口增加统一 token header 配置.
|
||||
*
|
||||
* @author zhaojun
|
||||
*/
|
||||
@Configuration
|
||||
public class Knife4jConfiguration {
|
||||
|
||||
@Bean
|
||||
public GroupedOpenApi groupedOpenApi() {
|
||||
String groupName = "前台功能";
|
||||
return GroupedOpenApi.builder()
|
||||
.group(groupName)
|
||||
.packagesToScan("im.zhaojun.zfile.module")
|
||||
.pathsToExclude("/admin/**")
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public GroupedOpenApi groupedOpenApi2() {
|
||||
String groupName = "管理员功能";
|
||||
return GroupedOpenApi.builder()
|
||||
.group(groupName)
|
||||
.packagesToScan("im.zhaojun.zfile.module")
|
||||
.pathsToMatch("/admin/**")
|
||||
.addOperationCustomizer(globalOperationCustomizer())
|
||||
.build();
|
||||
}
|
||||
|
||||
public OperationCustomizer globalOperationCustomizer() {
|
||||
return (operation, handlerMethod) -> {
|
||||
operation.addParametersItem(new HeaderParameter()
|
||||
.name("zfile-token")
|
||||
.description("token")
|
||||
.required(true)
|
||||
.schema(new StringSchema()));
|
||||
return operation;
|
||||
};
|
||||
}
|
||||
|
||||
@Bean
|
||||
public OpenAPI customOpenAPI() {
|
||||
Contact contact = new Contact();
|
||||
contact.setName("zhaojun");
|
||||
contact.setUrl("https://zfile.vip");
|
||||
contact.setEmail("873019219@qq.com");
|
||||
|
||||
return new OpenAPI()
|
||||
.info(new Info()
|
||||
.title("ZFILE 文档")
|
||||
.description("# 这是 ZFILE Restful 接口文档展示页面")
|
||||
.termsOfService("https://www.zfile.vip")
|
||||
.contact(contact)
|
||||
.version("1.0")
|
||||
.license(new License()
|
||||
.name("Apache 2.0")
|
||||
.url("http://doc.xiaominfo.com")));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package im.zhaojun.zfile.core.config.jackson;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* JSON String 反序列化器, 用于将 JSON 字符串反序列化为 JSON 对象.
|
||||
*
|
||||
* @author zhaojun
|
||||
*/
|
||||
public class JSONStringDeserializer extends JsonDeserializer<String> {
|
||||
|
||||
@Override
|
||||
public String deserialize(JsonParser p, DeserializationContext context) throws IOException {
|
||||
JsonNode node = p.getCodec().readTree(p);
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
return mapper.writeValueAsString(node);
|
||||
}
|
||||
|
||||
}
|
||||