diff --git a/.idea/.gitignore b/.idea/.gitignore
deleted file mode 100644
index 13566b8..0000000
--- a/.idea/.gitignore
+++ /dev/null
@@ -1,8 +0,0 @@
-# Default ignored files
-/shelf/
-/workspace.xml
-# Editor-based HTTP Client requests
-/httpRequests/
-# Datasource local storage ignored files
-/dataSources/
-/dataSources.local.xml
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
deleted file mode 100644
index d9baa12..0000000
--- a/.idea/compiler.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
deleted file mode 100644
index 34d0122..0000000
--- a/.idea/encodings.xml
+++ /dev/null
@@ -1,49 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml
deleted file mode 100644
index d752575..0000000
--- a/.idea/jarRepositories.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
deleted file mode 100644
index aac2f8b..0000000
--- a/.idea/misc.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/sonarlint/issuestore/1/f/1fd70d40f7c9cb50a47ee5948b3fd37458651d5f b/.idea/sonarlint/issuestore/1/f/1fd70d40f7c9cb50a47ee5948b3fd37458651d5f
new file mode 100644
index 0000000..e69de29
diff --git a/.idea/sonarlint/issuestore/5/0/5004442f77e22ef4c1d07b99806239bb7fb7ffbd b/.idea/sonarlint/issuestore/5/0/5004442f77e22ef4c1d07b99806239bb7fb7ffbd
new file mode 100644
index 0000000..e69de29
diff --git a/.idea/sonarlint/issuestore/5/f/5f3e07e732703a75dec38fdc223ff19f81693be2 b/.idea/sonarlint/issuestore/5/f/5f3e07e732703a75dec38fdc223ff19f81693be2
new file mode 100644
index 0000000..b7b9c52
--- /dev/null
+++ b/.idea/sonarlint/issuestore/5/f/5f3e07e732703a75dec38fdc223ff19f81693be2
@@ -0,0 +1,28 @@
+
+ java:S112^"FDefine and throw a dedicated exception instead of using a generic one.(J$a11442a9-8d63-4e07-b71d-8bbd99f23d2c
+
+java:S2293W"YReplace the type specification in this constructor call with the diamond operator ("<>").(J$416ded59-af84-4426-9b48-f83bd19950e0
+
+java:S2293`"YReplace the type specification in this constructor call with the diamond operator ("<>").(J$27d903c5-8337-45a5-9487-c9b6382b00de
+
+java:S2293j"YReplace the type specification in this constructor call with the diamond operator ("<>").(J$a16dd6cf-1b96-4717-87b7-c9f068b6fb5a
+
+java:S1192"FDefine a constant instead of duplicating this literal "roles" 3 times.(J$c6896070-0294-4abc-a161-3e23920dbf5e
+
+java:S1192"NDefine a constant instead of duplicating this literal "新增用户'" 3 times.(ӚJ$1d0d7412-1caf-4ba7-bedd-7f6977aef86f
+
+java:S1192"NDefine a constant instead of duplicating this literal "修改用户'" 3 times.(J$955f325a-7983-4ba4-b019-c635db935788
+~
+java:S68133"BRemove this field injection and use constructor injection instead.(J$18d02424-12fa-4fe9-9589-301064ff6621
+~
+java:S68136"BRemove this field injection and use constructor injection instead.(J$af97443c-b483-4714-a15e-8ff638d25d48
+~
+java:S68139"BRemove this field injection and use constructor injection instead.(J$304f05f5-990c-4db1-8ffc-9457d2e774de
+~
+java:S6813<"BRemove this field injection and use constructor injection instead.(J$8bfcda33-286c-4fb5-8121-10afaac59e76
+~
+java:S6813?"BRemove this field injection and use constructor injection instead.(J$cf9bc31d-c0ec-4699-8cad-87063e8cb226
+~
+java:S6813B"BRemove this field injection and use constructor injection instead.(J$f777a01b-edea-458c-bbb2-52a029ebf061
+
+java:S1130h"lRemove the declaration of thrown exception 'java.io.IOException', as it cannot be thrown from method's body.([J$5b9bbe61-57c5-4b05-ba1e-73a0abd75888
\ No newline at end of file
diff --git a/.idea/sonarlint/issuestore/7/c/7ce726513da4afdbc3421b816e8def845a39a0bb b/.idea/sonarlint/issuestore/7/c/7ce726513da4afdbc3421b816e8def845a39a0bb
new file mode 100644
index 0000000..e69de29
diff --git a/.idea/sonarlint/issuestore/8/4/840a68431ce1f8c33ff92941ebcdc18c832f00e4 b/.idea/sonarlint/issuestore/8/4/840a68431ce1f8c33ff92941ebcdc18c832f00e4
new file mode 100644
index 0000000..e69de29
diff --git a/.idea/sonarlint/issuestore/a/a/aa608ec83cbef0157ad034a45ce178fe8d3e1f1d b/.idea/sonarlint/issuestore/a/a/aa608ec83cbef0157ad034a45ce178fe8d3e1f1d
new file mode 100644
index 0000000..045b49d
--- /dev/null
+++ b/.idea/sonarlint/issuestore/a/a/aa608ec83cbef0157ad034a45ce178fe8d3e1f1d
@@ -0,0 +1,31 @@
+
+
+java:S2293"YReplace the type specification in this constructor call with the diamond operator ("<>").(J$e1a9f8da-eff9-4bdf-b97a-a600f6c356f7
+
+java:S2293"YReplace the type specification in this constructor call with the diamond operator ("<>").(힡J$e3523b3c-5ad8-499f-8ff7-bf9ed82d7f1a
+
+java:S1192"FDefine a constant instead of duplicating this literal "
" 4 times.(J$1ba3156b-b3a6-486b-9d57-6a7aa8850a41
+j
+java:S5411"(Use a primitive boolean expression here.(J$26408d15-1f4c-4cd2-a4f5-58b1b3e5fde6
+~
+java:S6813,"BRemove this field injection and use constructor injection instead.(J$a84cd146-4f87-4427-9d47-5069d78e2801
+~
+java:S6813/"BRemove this field injection and use constructor injection instead.(J$13bb241c-6bc4-4d7f-aac3-5d608cf5aeb1
+~
+java:S68132"BRemove this field injection and use constructor injection instead.(J$761aa5b4-f63b-437e-9f34-f77b55b48e13
+~
+java:S68135"BRemove this field injection and use constructor injection instead.(J$e6b2d573-8b11-428f-b5de-e38833a19041
+~
+java:S68138"BRemove this field injection and use constructor injection instead.(J$5fd175a1-26cb-4a75-a852-810b655c3dbe
+~
+java:S6813;"BRemove this field injection and use constructor injection instead.(J$cca9b812-26b4-43d0-b202-b70d706bcbc0
+~
+java:S6813>"BRemove this field injection and use constructor injection instead.(J$6a2f9c20-2af1-486f-b54d-ce8296e21114
+~
+java:S6813A"BRemove this field injection and use constructor injection instead.(J$5e5b493c-b4b9-4dbc-9e53-9db8ac63a56c
+
+java:S3252"JUse static access with "org.apache.commons.lang3.StringUtils" for "EMPTY".(J$0ae963e1-2cb5-4ae5-ae2b-44bc612a7c59
+
+java:S3252"JUse static access with "org.apache.commons.lang3.StringUtils" for "EMPTY".(J$6279aaf7-7b1f-46d0-bb4c-34c82cf9a516
+{
+java:S1155">Use isEmpty() to check whether the collection is empty or not.(J$084360c0-7ef2-403b-a292-12b957d31f5b
\ No newline at end of file
diff --git a/.idea/sonarlint/issuestore/c/7/c7c64afb3a3f5619d858fe5fe9a82458744eea6c b/.idea/sonarlint/issuestore/c/7/c7c64afb3a3f5619d858fe5fe9a82458744eea6c
new file mode 100644
index 0000000..e69de29
diff --git a/.idea/sonarlint/issuestore/d/5/d56c65f042936ed45626ea96a8a3be8a1f991518 b/.idea/sonarlint/issuestore/d/5/d56c65f042936ed45626ea96a8a3be8a1f991518
new file mode 100644
index 0000000..ceb12f6
--- /dev/null
+++ b/.idea/sonarlint/issuestore/d/5/d56c65f042936ed45626ea96a8a3be8a1f991518
@@ -0,0 +1,10 @@
+
+
+java:S6813"BRemove this field injection and use constructor injection instead.(8Δ2J$403f21c8-b418-475a-a8c0-81c2dc7a02d9
+
+java:S1128"CRemove this unused import 'com.bonus.file.utils.FileDownloadUtils'.(ޤ8Δ2J$f034c7f8-3d6e-4ecd-9b95-cd848a3a7999
+
+java:S1128
+"QRemove this unused import 'org.springframework.web.bind.annotation.RequestParam'.(¬8Δ2J$66d84852-c1a6-42a3-8a30-06506bcb8d2c
+x
+java:S1128"0Remove this unused import 'java.io.IOException'.(8Δ2J$372f4653-0162-4233-9cf3-fc20ef558e82
\ No newline at end of file
diff --git a/.idea/sonarlint/issuestore/e/6/e6de4bae11e5f329670d8eefa8f2efcb19f7ff88 b/.idea/sonarlint/issuestore/e/6/e6de4bae11e5f329670d8eefa8f2efcb19f7ff88
new file mode 100644
index 0000000..a06b796
--- /dev/null
+++ b/.idea/sonarlint/issuestore/e/6/e6de4bae11e5f329670d8eefa8f2efcb19f7ff88
@@ -0,0 +1,2 @@
+
+ java:S112"FDefine and throw a dedicated exception instead of using a generic one.(ܶJ$9b182cea-bc39-42f7-909d-809033563b29
\ No newline at end of file
diff --git a/.idea/sonarlint/issuestore/e/9/e9e9c8aa0ae1931dc6e028fc6aa2a4445d3ba19e b/.idea/sonarlint/issuestore/e/9/e9e9c8aa0ae1931dc6e028fc6aa2a4445d3ba19e
new file mode 100644
index 0000000..e69de29
diff --git a/.idea/sonarlint/issuestore/index.pb b/.idea/sonarlint/issuestore/index.pb
new file mode 100644
index 0000000..6149509
--- /dev/null
+++ b/.idea/sonarlint/issuestore/index.pb
@@ -0,0 +1,49 @@
+
+�
+Wbonus-modules/bonus-file/src/main/java/com/bonus/file/controller/SysFileController.java,d/5/d56c65f042936ed45626ea96a8a3be8a1f991518
+7
+pom.xml,4/4/442292b8a7efeabbe4cc176709b833b1792140ec
+�
+Qbonus-modules/bonus-file/src/main/java/com/bonus/file/config/ResourcesConfig.java,7/c/7ce726513da4afdbc3421b816e8def845a39a0bb
+A
+bonus-api/pom.xml,4/d/4d66766070d502e9495acbf654cd20829c4425b3
+�
+^bonus-modules/bonus-system/src/main/java/com/bonus/system/service/impl/SysUserServiceImpl.java,a/a/aa608ec83cbef0157ad034a45ce178fe8d3e1f1d
+�
+Vbonus-modules/bonus-system/src/main/java/com/bonus/system/service/ISysUserService.java,e/9/e9e9c8aa0ae1931dc6e028fc6aa2a4445d3ba19e
+}
+Mbonus-modules/bonus-file/src/main/java/com/bonus/file/config/MinioConfig.java,5/0/5004442f77e22ef4c1d07b99806239bb7fb7ffbd
+
+Obonus-modules/bonus-file/src/main/java/com/bonus/file/BonusFileApplication.java,1/f/1fd70d40f7c9cb50a47ee5948b3fd37458651d5f
+R
+"bonus-visual/bonus-monitor/pom.xml,a/5/a567507002bfc4cb69f800244bcc422fbcc10e5d
+�
+[bonus-modules/bonus-system/src/main/java/com/bonus/system/controller/SysUserController.java,5/f/5f3e07e732703a75dec38fdc223ff19f81693be2
+�
+Rbonus-modules/bonus-file/src/main/java/com/bonus/file/utils/FileDownloadUtils.java,9/7/9722eda79f36a8f93bcf90763a02040c63aa2fde
+�
+Tbonus-api/bonus-api-system/src/main/java/com/bonus/system/api/RemoteFileService.java,8/4/840a68431ce1f8c33ff92941ebcdc18c832f00e4
+~
+Nbonus-gateway/src/main/java/com/bonus/gateway/service/ValidateCodeService.java,d/6/d6fecdfdef18b3d6832231c6f1b018c886d4fab8
+z
+Jbonus-gateway/src/main/java/com/bonus/gateway/BonusGatewayApplication.java,2/c/2cc24e5aa9771bcc9b5a17544600b0e244d9c2ad
+q
+Abonus-auth/src/main/java/com/bonus/auth/BonusAuthApplication.java,9/1/911ccbfd7be6b58e893061cdfb63c958e37d6eda
+D
+sql/bns_20240604.sql,8/5/85b1974774f16ce7959a122e4766c9a31a7a7895
+�
+\bonus-modules/bonus-file/src/main/java/com/bonus/file/service/FastDfsSysFileServiceImpl.java,c/6/c6d854777bca75255487810d12805069bf1a4f45
+P
+ bonus-modules/bonus-file/pom.xml,0/f/0fbb26476b87bedf6f864aa5d52284b75bde6869
+�
+Ubonus-modules/bonus-system/src/main/java/com/bonus/system/BonusSystemApplication.java,a/7/a7445a685a0b55cc0d903c370a10674cfbd15608
+E
+bonus-modules/pom.xml,7/3/73fe31f1bc837e4395fd9296f4f1793cc44fbd07
+�
+Pbonus-modules/bonus-file/src/main/java/com/bonus/file/utils/FileUploadUtils.java,c/9/c9d1df9f46abbc0b2e5a3d147c7b3407ef09b84e
+�
+Rbonus-modules/bonus-file/src/main/java/com/bonus/file/service/ISysFileService.java,6/7/6796766c5f6d21c9e346f89b25160c0dd6401a5b
+�
+Zbonus-modules/bonus-file/src/main/java/com/bonus/file/service/MinioSysFileServiceImpl.java,8/7/8721859fd7a6cfb3d43a453ba3850048c6eeb2d2
+�
+Zbonus-modules/bonus-file/src/main/java/com/bonus/file/service/LocalSysFileServiceImpl.java,2/e/2ef247d050a0881cb49e4a582bc1a311f9bc6be0
\ No newline at end of file
diff --git a/.idea/sonarlint/securityhotspotstore/1/f/1fd70d40f7c9cb50a47ee5948b3fd37458651d5f b/.idea/sonarlint/securityhotspotstore/1/f/1fd70d40f7c9cb50a47ee5948b3fd37458651d5f
new file mode 100644
index 0000000..e69de29
diff --git a/.idea/sonarlint/securityhotspotstore/5/0/5004442f77e22ef4c1d07b99806239bb7fb7ffbd b/.idea/sonarlint/securityhotspotstore/5/0/5004442f77e22ef4c1d07b99806239bb7fb7ffbd
new file mode 100644
index 0000000..e69de29
diff --git a/.idea/sonarlint/securityhotspotstore/5/f/5f3e07e732703a75dec38fdc223ff19f81693be2 b/.idea/sonarlint/securityhotspotstore/5/f/5f3e07e732703a75dec38fdc223ff19f81693be2
new file mode 100644
index 0000000..e69de29
diff --git a/.idea/sonarlint/securityhotspotstore/7/c/7ce726513da4afdbc3421b816e8def845a39a0bb b/.idea/sonarlint/securityhotspotstore/7/c/7ce726513da4afdbc3421b816e8def845a39a0bb
new file mode 100644
index 0000000..e69de29
diff --git a/.idea/sonarlint/securityhotspotstore/8/4/840a68431ce1f8c33ff92941ebcdc18c832f00e4 b/.idea/sonarlint/securityhotspotstore/8/4/840a68431ce1f8c33ff92941ebcdc18c832f00e4
new file mode 100644
index 0000000..e69de29
diff --git a/.idea/sonarlint/securityhotspotstore/a/a/aa608ec83cbef0157ad034a45ce178fe8d3e1f1d b/.idea/sonarlint/securityhotspotstore/a/a/aa608ec83cbef0157ad034a45ce178fe8d3e1f1d
new file mode 100644
index 0000000..e69de29
diff --git a/.idea/sonarlint/securityhotspotstore/c/7/c7c64afb3a3f5619d858fe5fe9a82458744eea6c b/.idea/sonarlint/securityhotspotstore/c/7/c7c64afb3a3f5619d858fe5fe9a82458744eea6c
new file mode 100644
index 0000000..e69de29
diff --git a/.idea/sonarlint/securityhotspotstore/d/5/d56c65f042936ed45626ea96a8a3be8a1f991518 b/.idea/sonarlint/securityhotspotstore/d/5/d56c65f042936ed45626ea96a8a3be8a1f991518
new file mode 100644
index 0000000..e69de29
diff --git a/.idea/sonarlint/securityhotspotstore/e/6/e6de4bae11e5f329670d8eefa8f2efcb19f7ff88 b/.idea/sonarlint/securityhotspotstore/e/6/e6de4bae11e5f329670d8eefa8f2efcb19f7ff88
new file mode 100644
index 0000000..e69de29
diff --git a/.idea/sonarlint/securityhotspotstore/e/9/e9e9c8aa0ae1931dc6e028fc6aa2a4445d3ba19e b/.idea/sonarlint/securityhotspotstore/e/9/e9e9c8aa0ae1931dc6e028fc6aa2a4445d3ba19e
new file mode 100644
index 0000000..e69de29
diff --git a/.idea/sonarlint/securityhotspotstore/index.pb b/.idea/sonarlint/securityhotspotstore/index.pb
new file mode 100644
index 0000000..6149509
--- /dev/null
+++ b/.idea/sonarlint/securityhotspotstore/index.pb
@@ -0,0 +1,49 @@
+
+�
+Wbonus-modules/bonus-file/src/main/java/com/bonus/file/controller/SysFileController.java,d/5/d56c65f042936ed45626ea96a8a3be8a1f991518
+7
+pom.xml,4/4/442292b8a7efeabbe4cc176709b833b1792140ec
+�
+Qbonus-modules/bonus-file/src/main/java/com/bonus/file/config/ResourcesConfig.java,7/c/7ce726513da4afdbc3421b816e8def845a39a0bb
+A
+bonus-api/pom.xml,4/d/4d66766070d502e9495acbf654cd20829c4425b3
+�
+^bonus-modules/bonus-system/src/main/java/com/bonus/system/service/impl/SysUserServiceImpl.java,a/a/aa608ec83cbef0157ad034a45ce178fe8d3e1f1d
+�
+Vbonus-modules/bonus-system/src/main/java/com/bonus/system/service/ISysUserService.java,e/9/e9e9c8aa0ae1931dc6e028fc6aa2a4445d3ba19e
+}
+Mbonus-modules/bonus-file/src/main/java/com/bonus/file/config/MinioConfig.java,5/0/5004442f77e22ef4c1d07b99806239bb7fb7ffbd
+
+Obonus-modules/bonus-file/src/main/java/com/bonus/file/BonusFileApplication.java,1/f/1fd70d40f7c9cb50a47ee5948b3fd37458651d5f
+R
+"bonus-visual/bonus-monitor/pom.xml,a/5/a567507002bfc4cb69f800244bcc422fbcc10e5d
+�
+[bonus-modules/bonus-system/src/main/java/com/bonus/system/controller/SysUserController.java,5/f/5f3e07e732703a75dec38fdc223ff19f81693be2
+�
+Rbonus-modules/bonus-file/src/main/java/com/bonus/file/utils/FileDownloadUtils.java,9/7/9722eda79f36a8f93bcf90763a02040c63aa2fde
+�
+Tbonus-api/bonus-api-system/src/main/java/com/bonus/system/api/RemoteFileService.java,8/4/840a68431ce1f8c33ff92941ebcdc18c832f00e4
+~
+Nbonus-gateway/src/main/java/com/bonus/gateway/service/ValidateCodeService.java,d/6/d6fecdfdef18b3d6832231c6f1b018c886d4fab8
+z
+Jbonus-gateway/src/main/java/com/bonus/gateway/BonusGatewayApplication.java,2/c/2cc24e5aa9771bcc9b5a17544600b0e244d9c2ad
+q
+Abonus-auth/src/main/java/com/bonus/auth/BonusAuthApplication.java,9/1/911ccbfd7be6b58e893061cdfb63c958e37d6eda
+D
+sql/bns_20240604.sql,8/5/85b1974774f16ce7959a122e4766c9a31a7a7895
+�
+\bonus-modules/bonus-file/src/main/java/com/bonus/file/service/FastDfsSysFileServiceImpl.java,c/6/c6d854777bca75255487810d12805069bf1a4f45
+P
+ bonus-modules/bonus-file/pom.xml,0/f/0fbb26476b87bedf6f864aa5d52284b75bde6869
+�
+Ubonus-modules/bonus-system/src/main/java/com/bonus/system/BonusSystemApplication.java,a/7/a7445a685a0b55cc0d903c370a10674cfbd15608
+E
+bonus-modules/pom.xml,7/3/73fe31f1bc837e4395fd9296f4f1793cc44fbd07
+�
+Pbonus-modules/bonus-file/src/main/java/com/bonus/file/utils/FileUploadUtils.java,c/9/c9d1df9f46abbc0b2e5a3d147c7b3407ef09b84e
+�
+Rbonus-modules/bonus-file/src/main/java/com/bonus/file/service/ISysFileService.java,6/7/6796766c5f6d21c9e346f89b25160c0dd6401a5b
+�
+Zbonus-modules/bonus-file/src/main/java/com/bonus/file/service/MinioSysFileServiceImpl.java,8/7/8721859fd7a6cfb3d43a453ba3850048c6eeb2d2
+�
+Zbonus-modules/bonus-file/src/main/java/com/bonus/file/service/LocalSysFileServiceImpl.java,2/e/2ef247d050a0881cb49e4a582bc1a311f9bc6be0
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
deleted file mode 100644
index 94a25f7..0000000
--- a/.idea/vcs.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/bonus-common/bonus-common-core/src/main/java/com/bonus/common/core/utils/Base64Utils.java b/bonus-common/bonus-common-core/src/main/java/com/bonus/common/core/utils/Base64Utils.java
new file mode 100644
index 0000000..88e5805
--- /dev/null
+++ b/bonus-common/bonus-common-core/src/main/java/com/bonus/common/core/utils/Base64Utils.java
@@ -0,0 +1,67 @@
+package com.bonus.common.core.utils;
+
+import org.apache.commons.lang3.StringUtils;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+
+public class Base64Utils {
+ private static final String BASE64_MSG = "base64";
+ /**
+ * 将 Base64 字符串解码,再解码URL参数, 默认使用 UTF-8
+ * @param source 原始 Base64 字符串
+ * @return decoded string
+ *
+ * aHR0cHM6Ly9maWxlLmtla2luZy5jbi9kZW1vL%2BS4reaWhy5wcHR4 -> https://file.keking.cn/demo/%E4%B8%AD%E6%96%87.pptx -> https://file.keking.cn/demo/中文.pptx
+ */
+ public static String decodeUrl(String source) {
+ String url = decodeBase64String(source, StandardCharsets.UTF_8);
+ if (! StringUtils.isNotBlank(url)){
+ return null;
+ }
+ return url;
+ }
+
+ /**
+ * 将 Base64 字符串使用指定字符集解码
+ * @param source 原始 Base64 字符串
+ * @param charsets 字符集
+ * @return decoded string
+ */
+ public static String decodeBase64String(String source, Charset charsets) {
+ /*
+ * url 传入的参数里加号会被替换成空格,导致解析出错,这里需要把空格替换回加号
+ * 有些 Base64 实现可能每 76 个字符插入换行符,也一并去掉
+ * https://github.com/kekingcn/kkFileView/pull/340
+ */
+ try {
+ return new String(org.springframework.util.Base64Utils.decodeFromString(source.replaceAll(" ", "+").replaceAll("\n", "")), charsets);
+ } catch (Exception e) {
+ if (e.getMessage().toLowerCase().contains(BASE64_MSG)) {
+ System.out.println("url解码异常,接入方法错误未使用BASE64");
+ }else {
+ System.out.println("url解码异常,其他错误,"+ e);
+ }
+ return null;
+ }
+ }
+
+
+ public static String getFileNameFromURL(String url) {
+ if (url.toLowerCase().startsWith("file:")) {
+ try {
+ URL urlObj = new URL(url);
+ url = urlObj.getPath().substring(1);
+ } catch (MalformedURLException e) {
+ e.printStackTrace();
+ }
+ }
+ // 因为url的参数中可能会存在/的情况,所以直接url.lastIndexOf("/")会有问题
+ // 所以先从?处将url截断,然后运用url.lastIndexOf("/")获取文件名
+ String noQueryUrl = url.substring(0, url.contains("?") ? url.indexOf("?") : url.length());
+ return noQueryUrl.substring(noQueryUrl.lastIndexOf("/") + 1);
+ }
+
+}
diff --git a/bonus-modules/bonus-file/src/.DS_Store b/bonus-modules/bonus-file/src/.DS_Store
new file mode 100644
index 0000000..7b0d367
Binary files /dev/null and b/bonus-modules/bonus-file/src/.DS_Store differ
diff --git a/bonus-modules/bonus-file/src/main/.DS_Store b/bonus-modules/bonus-file/src/main/.DS_Store
new file mode 100644
index 0000000..508c44c
Binary files /dev/null and b/bonus-modules/bonus-file/src/main/.DS_Store differ
diff --git a/bonus-modules/bonus-file/src/main/java/.DS_Store b/bonus-modules/bonus-file/src/main/java/.DS_Store
new file mode 100644
index 0000000..35a54bf
Binary files /dev/null and b/bonus-modules/bonus-file/src/main/java/.DS_Store differ
diff --git a/bonus-modules/bonus-file/src/main/java/com/.DS_Store b/bonus-modules/bonus-file/src/main/java/com/.DS_Store
new file mode 100644
index 0000000..30bf7fc
Binary files /dev/null and b/bonus-modules/bonus-file/src/main/java/com/.DS_Store differ
diff --git a/bonus-modules/bonus-file/src/main/java/com/bonus/.DS_Store b/bonus-modules/bonus-file/src/main/java/com/bonus/.DS_Store
new file mode 100644
index 0000000..84716e9
Binary files /dev/null and b/bonus-modules/bonus-file/src/main/java/com/bonus/.DS_Store differ
diff --git a/bonus-modules/bonus-file/src/main/java/com/bonus/file/.DS_Store b/bonus-modules/bonus-file/src/main/java/com/bonus/file/.DS_Store
new file mode 100644
index 0000000..d185717
Binary files /dev/null and b/bonus-modules/bonus-file/src/main/java/com/bonus/file/.DS_Store differ
diff --git a/bonus-modules/bonus-file/src/main/java/com/bonus/file/utils/FileDownloadUtils.java b/bonus-modules/bonus-file/src/main/java/com/bonus/file/utils/FileDownloadUtils.java
new file mode 100644
index 0000000..88e267b
--- /dev/null
+++ b/bonus-modules/bonus-file/src/main/java/com/bonus/file/utils/FileDownloadUtils.java
@@ -0,0 +1,106 @@
+package com.bonus.file.utils;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+import org.apache.commons.net.ftp.FTP;
+import org.apache.commons.net.ftp.FTPClient;
+
+public class FileDownloadUtils {
+
+ public static boolean downloadFile(String urlStr, String destination) throws IOException {
+ if (urlStr.startsWith("http://") || urlStr.startsWith("https://")) {
+ return downloadHttpFile(urlStr, destination);
+ } else if (urlStr.startsWith("ftp://")) {
+ return downloadFtpFile(urlStr, destination);
+ } else {
+ throw new IllegalArgumentException("Unsupported protocol: " + urlStr);
+ }
+ }
+
+ private static boolean downloadHttpFile(String urlStr, String destination) throws IOException {
+ URL url = new URL(urlStr);
+ HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+ connection.setRequestMethod("GET");
+
+ try (InputStream inputStream = connection.getInputStream();
+ FileOutputStream outputStream = new FileOutputStream(destination)) {
+
+ byte[] buffer = new byte[4096];
+ int bytesRead = -1;
+ while ((bytesRead = inputStream.read(buffer)) != -1) {
+ outputStream.write(buffer, 0, bytesRead);
+ }
+ System.out.println("HTTP download completed: " + destination);
+ } finally {
+ connection.disconnect();
+ }
+ return true;
+ }
+
+ private static boolean downloadFtpFile(String urlStr, String destination) throws IOException {
+ FTPClient ftpClient = new FTPClient();
+ try {
+ URL url = new URL(urlStr);
+ String host = url.getHost();
+ int port = url.getPort() == -1 ? 21 : url.getPort();
+ String userInfo = url.getUserInfo();
+ String[] credentials = userInfo != null ? userInfo.split(":") : new String[]{"anonymous", ""};
+ String username = credentials[0];
+ String password = credentials.length > 1 ? credentials[1] : "";
+ String remoteFilePath = url.getPath();
+
+ ftpClient.connect(host, port);
+ ftpClient.login(username, password);
+ ftpClient.enterLocalPassiveMode();
+ ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
+
+ try (InputStream inputStream = ftpClient.retrieveFileStream(remoteFilePath);
+ FileOutputStream outputStream = new FileOutputStream(destination)) {
+
+ byte[] buffer = new byte[4096];
+ int bytesRead = -1;
+ while ((bytesRead = inputStream.read(buffer)) != -1) {
+ outputStream.write(buffer, 0, bytesRead);
+ }
+
+ if (ftpClient.completePendingCommand()) {
+ System.out.println("FTP download completed: " + destination);
+ } else {
+ System.out.println("Failed to complete FTP download: " + destination);
+ }
+ }
+ } finally {
+ ftpClient.logout();
+ ftpClient.disconnect();
+ }
+ return true;
+ }
+
+// public static String replaceUrlPrefix(String originalUrl, String newPrefix) {
+// // 使用正则表达式匹配 URL 中的 "statics" 及其之前的部分
+// String regex = "^(.*?/statics)";
+// return originalUrl.replaceFirst(regex, newPrefix);
+// }
+
+// public static void main(String[] args) {
+// try {
+// // 示例下载文件
+// String httpUrl = "http://example.com/file.txt";
+// String ftpUrl = "ftp://username:password@ftp.example.com/file.txt";
+// String destination = "downloaded_file.txt";
+//
+// // 下载HTTP文件
+// downloadFile(httpUrl, destination);
+//
+// // 下载FTP文件
+// downloadFile(ftpUrl, destination);
+//
+// } catch (IOException ex) {
+// ex.printStackTrace();
+// }
+// }
+}