Pārlūkot izejas kodu

ufop 代码提交

pans 7 mēneši atpakaļ
vecāks
revīzija
ce6ffb825f
85 mainītis faili ar 4513 papildinājumiem un 0 dzēšanām
  1. 26 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/.gitignore
  2. 21 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/LICENSE
  3. 133 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/README.md
  4. 116 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/pom.xml
  5. 101 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/autoconfiguration/UFOPAutoConfiguration.java
  6. 21 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/autoconfiguration/UFOPProperties.java
  7. 11 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/config/AliyunConfig.java
  8. 11 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/config/MinioConfig.java
  9. 9 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/config/QiniuyunConfig.java
  10. 27 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/constant/FilePermissionEnum.java
  11. 27 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/constant/StorageTypeEnum.java
  12. 25 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/constant/UploadFileStatusEnum.java
  13. 13 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/domain/AliyunOSS.java
  14. 12 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/domain/QiniuyunKodo.java
  15. 9 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/domain/ThumbImage.java
  16. 16 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/exception/UFOPException.java
  17. 18 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/exception/operation/CopyException.java
  18. 16 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/exception/operation/DeleteException.java
  19. 16 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/exception/operation/DownloadException.java
  20. 16 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/exception/operation/PreviewException.java
  21. 15 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/exception/operation/ReadException.java
  22. 16 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/exception/operation/UploadException.java
  23. 15 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/exception/operation/WriteException.java
  24. 180 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/factory/UFOPFactory.java
  25. 15 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/copy/Copier.java
  26. 8 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/copy/domain/CopyFile.java
  27. 39 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/copy/product/AliyunOSSCopier.java
  28. 30 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/copy/product/FastDFSCopier.java
  29. 32 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/copy/product/LocalStorageCopier.java
  30. 65 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/copy/product/MinioCopier.java
  31. 83 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/copy/product/QiniuyunKodoCopier.java
  32. 25 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/delete/Deleter.java
  33. 8 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/delete/domain/DeleteFile.java
  34. 31 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/delete/product/AliyunOSSDeleter.java
  35. 25 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/delete/product/FastDFSDeleter.java
  36. 25 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/delete/product/LocalStorageDeleter.java
  37. 47 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/delete/product/MinioDeleter.java
  38. 39 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/delete/product/QiniuyunKodoDeleter.java
  39. 34 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/download/Downloader.java
  40. 11 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/download/domain/DownloadFile.java
  41. 13 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/download/domain/Range.java
  42. 51 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/download/product/AliyunOSSDownloader.java
  43. 37 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/download/product/FastDFSDownloader.java
  44. 43 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/download/product/LocalStorageDownloader.java
  45. 63 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/download/product/MinioDownloader.java
  46. 48 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/download/product/QiniuyunKodoDownloader.java
  47. 184 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/preview/Previewer.java
  48. 10 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/preview/domain/PreviewFile.java
  49. 45 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/preview/product/AliyunOSSPreviewer.java
  50. 37 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/preview/product/FastDFSPreviewer.java
  51. 47 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/preview/product/LocalStoragePreviewer.java
  52. 56 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/preview/product/MinioPreviewer.java
  53. 44 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/preview/product/QiniuyunKodoPreviewer.java
  54. 7 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/read/Reader.java
  55. 8 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/read/domain/ReadFile.java
  56. 53 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/read/product/AliyunOSSReader.java
  57. 43 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/read/product/FastDFSReader.java
  58. 31 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/read/product/LocalStorageReader.java
  59. 65 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/read/product/MinioReader.java
  60. 49 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/read/product/QiniuyunKodoReader.java
  61. 208 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/upload/Uploader.java
  62. 17 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/upload/domain/UploadFile.java
  63. 10 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/upload/domain/UploadFileInfo.java
  64. 20 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/upload/domain/UploadFileResult.java
  65. 185 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/upload/product/AliyunOSSUploader.java
  66. 140 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/upload/product/FastDFSUploader.java
  67. 130 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/upload/product/LocalStorageUploader.java
  68. 145 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/upload/product/MinioUploader.java
  69. 165 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/upload/product/QiniuyunKodoUploader.java
  70. 60 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/upload/request/QiwenMultipartFile.java
  71. 9 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/write/Writer.java
  72. 9 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/write/domain/WriteFile.java
  73. 34 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/write/product/AliyunOSSWriter.java
  74. 22 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/write/product/FastDFSWriter.java
  75. 31 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/write/product/LocalStorageWriter.java
  76. 55 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/write/product/MinioWriter.java
  77. 65 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/write/product/QiniuyunKodoWriter.java
  78. 16 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/util/AliyunUtils.java
  79. 119 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/util/CharsetUtils.java
  80. 28 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/util/QiniuyunUtils.java
  81. 214 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/util/ReadFileUtils.java
  82. 71 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/util/RedisUtil.java
  83. 198 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/util/UFOPUtils.java
  84. 239 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/util/concurrent/locks/RedisLock.java
  85. 2 0
      snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/resources/META-INF/spring.factories

+ 26 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/.gitignore

@@ -0,0 +1,26 @@
+# Compiled class file
+*.class
+
+# Log file
+*.log
+
+# BlueJ files
+*.ctxt
+
+# Mobile Tools for Java (J2ME)
+.mtj.tmp/
+
+# Package Files #
+*.jar
+*.war
+*.nar
+*.ear
+*.zip
+*.tar.gz
+*.rar
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+hs_err_pid*
+/.idea
+/*.iml
+/target

+ 21 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2021 MAC
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 133 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/README.md

@@ -0,0 +1,133 @@
+# ufop-spring-boot-starter
+
+#### 介绍
+
+UFOP (Unified File Operation Platform) 统一文件操作平台,通过引入该依赖,可以实现文件操作的统一管理
+
+此项目为奇文网盘核心功能,之前有不少人咨询,如何将网盘集成到自己的项目?出于这个目的,就把这块功能剥离出来供大家方便引入,目前实现的主要功能如下:
+
+1. 本地文件上传、下载,删除,预览,重命名,读文件流,写文件流
+2. 阿里云OSS上传,下载,删除,预览,重命名,读文件流,写文件流
+3. FastDFS上传,下载,删除,预览,重命名,读文件流,写文件流
+4. FastDFS+Redis实现集群化部署
+5. 图片支持缩略图预览
+
+
+#### 软件架构
+#### 安装教程
+mvn clean install 
+#### 使用说明
+
+1.  引入pom依赖
+
+这里的具体版本号建议引入最新版本
+```xml
+<dependency>
+    <groupId>com.qiwenshare</groupId>
+    <artifactId>ufop-spring-boot-starter</artifactId>
+    <version>{new version}<version>
+</dependency>
+```
+2.  application.properties配置文件说明
+
+配置磁盘存储方式, 0-本地存储, 1-阿里云OSS存储, 2-fastDFS存储, 3-minio存储, 4-七牛云KODO对象存储
+
+```properties
+ufop.storage-type=0
+```
+
+当选择0-本地磁盘存储之后,你还可以继续配置本地文件存储路径
+```properties
+ufop.local-storage-path=D://test
+```
+当选择1-阿里云OSS存储之后,需要配置阿里云OSS相关信息,
+```properties
+#阿里云oss基本配置
+ufop.aliyun.oss.endpoint=
+ufop.aliyun.oss.access-key-id=
+ufop.aliyun.oss.access-key-secret=
+ufop.aliyun.oss.bucket-name=
+```
+当选择2-FastDFS存储之后,则需要配置FastDFS服务器信息
+
+```properties
+#FastDFS配置
+fdfs.so-timeout=1501
+fdfs.connect-timeout=601
+fdfs.thumb-image.width=150
+fdfs.thumb-image.height=150
+fdfs.tracker-list=127.0.0.1:22122 
+```
+其他存储方式可在下方链接查看
+https://pan.qiwenshare.com/docs/config/#%E5%AD%98%E5%82%A8%E6%96%B9%E5%BC%8F%E9%85%8D%E7%BD%AE
+
+除了0-本地存储外,其他存储方式需要配置redis信息
+```properties
+
+# Redis数据库索引(默认为0)
+spring.redis.database=0  
+# Redis服务器地址
+spring.redis.host=127.0.0.1
+# Redis服务器连接端口
+spring.redis.port=6379
+# Redis服务器连接密码(默认为空)
+spring.redis.password=ma123456
+# 连接池最大连接数(使用负值表示没有限制) 默认 8
+spring.redis.lettuce.pool.max-active=8
+# 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1
+spring.redis.lettuce.pool.max-wait=10000
+# 连接池中的最大空闲连接 默认 8
+spring.redis.lettuce.pool.max-idle=30
+# 连接池中的最小空闲连接 默认 0
+spring.redis.lettuce.pool.min-idle=10
+#连接超时时间(毫秒)
+spring.redis.timeout=5000
+```
+
+
+
+当配置完基础信息之后,使用就非常简单了,伪代码如下:
+
+注入UFOPFactory
+```java
+@Resource
+UFOPFactory ufopFactory;
+```
+上传文件操作,具体这个上传操作是哪种存储实现,由`ufop.storage-type`配置项决定,
+
+```java
+
+//上传操作
+Uploader uploader = ufopFactory.getUploader();
+uploader.upload(request, uploadFile);
+```
+
+下载和删除则需要用户自己传入文件存储类型
+```java
+//下载操作
+Downloader downloader = ufopFactory.getDownloader(fileBean.getStorageType());
+downloader.download(httpServletResponse, downloadFile);
+//删除操作
+Deleter deleter = ufopFactory.getDeleter(fileBean.getStorageType());
+deleter.delete(deleteFile);
+
+```
+
+该工程目前已经在奇文网盘运行了一年多时间,但不免还是会有很多缺陷,如果遇到问题,欢迎大家参与贡献,一块去完善它
+
+#### 参与贡献
+
+1.  Fork 本仓库
+2.  新建 Feat_xxx 分支
+3.  提交代码
+4.  新建 Pull Request
+
+
+#### 特技
+
+1.  使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md
+2.  Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com)
+3.  你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目
+4.  [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目
+5.  Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help)
+6.  Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)

+ 116 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/pom.xml

@@ -0,0 +1,116 @@
+<?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">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>com.qiwenshare</groupId>
+        <artifactId>qiwenshare</artifactId>
+        <version>1.2.8</version>
+        <relativePath/>
+    </parent>
+    <artifactId>ufop-spring-boot-starter</artifactId>
+    <version>${project.parent.version}</version>
+    <packaging>jar</packaging>
+
+    <name>ufop-spring-boot-starter</name>
+    <description>ufop-spring-boot-starter</description>
+    <url>https://gitee.com/qiwen-cloud/ufop-spring-boot-starter</url>
+
+    <repositories>
+        <repository>
+            <id>com.e-iceblue</id>
+            <url>http://repo.e-iceblue.cn/repository/maven-public/</url>
+        </repository>
+    </repositories>
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-autoconfigure</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-configuration-processor</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-redis</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.github.tobato</groupId>
+            <artifactId>fastdfs-client</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.aliyun.oss</groupId>
+            <artifactId>aliyun-sdk-oss</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.qiwenshare</groupId>
+            <artifactId>qiwenshare-common</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.poi</groupId>
+            <artifactId>poi</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.poi</groupId>
+            <artifactId>poi-ooxml</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.poi</groupId>
+            <artifactId>poi-scratchpad</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.pdfbox</groupId>
+            <artifactId>pdfbox</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.minio</groupId>
+            <artifactId>minio</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.qiniu</groupId>
+            <artifactId>qiniu-java-sdk</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.squareup.okhttp3</groupId>
+            <artifactId>okhttp</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-http</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>commons-fileupload</groupId>
+            <artifactId>commons-fileupload</artifactId>
+        </dependency>
+
+
+
+    </dependencies>
+
+
+
+</project>

+ 101 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/autoconfiguration/UFOPAutoConfiguration.java

@@ -0,0 +1,101 @@
+package com.qiwenshare.ufop.autoconfiguration;
+
+import com.github.tobato.fastdfs.FdfsClientConfig;
+import com.qiwenshare.ufop.factory.UFOPFactory;
+import com.qiwenshare.ufop.operation.copy.product.FastDFSCopier;
+import com.qiwenshare.ufop.operation.delete.product.FastDFSDeleter;
+import com.qiwenshare.ufop.operation.download.product.FastDFSDownloader;
+import com.qiwenshare.ufop.operation.preview.product.FastDFSPreviewer;
+import com.qiwenshare.ufop.operation.read.product.FastDFSReader;
+import com.qiwenshare.ufop.operation.upload.product.AliyunOSSUploader;
+import com.qiwenshare.ufop.operation.upload.product.FastDFSUploader;
+import com.qiwenshare.ufop.operation.upload.product.MinioUploader;
+import com.qiwenshare.ufop.operation.upload.product.QiniuyunKodoUploader;
+import com.qiwenshare.ufop.operation.write.product.FastDFSWriter;
+import com.qiwenshare.ufop.util.RedisUtil;
+import com.qiwenshare.ufop.util.UFOPUtils;
+import com.qiwenshare.ufop.util.concurrent.locks.RedisLock;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.EnableMBeanExport;
+import org.springframework.context.annotation.Import;
+import org.springframework.jmx.support.RegistrationPolicy;
+
+
+@Slf4j
+@Configuration
+//@ConditionalOnClass(UFOService.class)
+@EnableConfigurationProperties({UFOPProperties.class})
+@Import(FdfsClientConfig.class)
+@EnableMBeanExport(registration = RegistrationPolicy.IGNORE_EXISTING)
+public class UFOPAutoConfiguration {
+
+    @Autowired
+    private UFOPProperties ufopProperties;
+
+    @Bean
+    public UFOPFactory ufopFactory() {
+        UFOPUtils.LOCAL_STORAGE_PATH = ufopProperties.getLocalStoragePath();
+        String bucketName = ufopProperties.getBucketName();
+        if (StringUtils.isNotEmpty(bucketName)) {
+            UFOPUtils.ROOT_PATH = ufopProperties.getBucketName();
+        } else {
+            UFOPUtils.ROOT_PATH = "upload";
+        }
+        return new UFOPFactory(ufopProperties);
+    }
+    @Bean
+    public FastDFSCopier fastDFSCreater() {
+        return new FastDFSCopier();
+    }
+    @Bean
+    public FastDFSUploader fastDFSUploader() {
+        return new FastDFSUploader();
+    }
+    @Bean
+    public FastDFSDownloader fastDFSDownloader() {
+        return new FastDFSDownloader();
+    }
+    @Bean
+    public FastDFSDeleter fastDFSDeleter() {
+        return new FastDFSDeleter();
+    }
+    @Bean
+    public FastDFSReader fastDFSReader() {
+        return new FastDFSReader();
+    }
+    @Bean
+    public FastDFSWriter fastDFSWriter() {
+        return new FastDFSWriter();
+    }
+    @Bean
+    public FastDFSPreviewer fastDFSPreviewer() {
+        return new FastDFSPreviewer(ufopProperties.getThumbImage());
+    }
+    @Bean
+    public AliyunOSSUploader aliyunOSSUploader() {
+        return new AliyunOSSUploader(ufopProperties.getAliyun());
+    }
+    @Bean
+    public MinioUploader minioUploader() {
+        return new MinioUploader(ufopProperties.getMinio());
+    }
+    @Bean
+    public QiniuyunKodoUploader qiniuyunKodoUploader() {
+        return new QiniuyunKodoUploader(ufopProperties.getQiniuyun());
+    }
+
+    @Bean
+    public RedisLock redisLock() {
+        return new RedisLock();
+    }
+    @Bean
+    public RedisUtil redisUtil() {
+        return new RedisUtil();
+    }
+
+}

+ 21 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/autoconfiguration/UFOPProperties.java

@@ -0,0 +1,21 @@
+package com.qiwenshare.ufop.autoconfiguration;
+
+import com.qiwenshare.ufop.config.AliyunConfig;
+import com.qiwenshare.ufop.config.MinioConfig;
+import com.qiwenshare.ufop.config.QiniuyunConfig;
+import com.qiwenshare.ufop.domain.ThumbImage;
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+@Data
+@ConfigurationProperties(prefix = "ufop")
+public class UFOPProperties {
+
+    private String bucketName;
+    private String storageType;
+    private String localStoragePath;
+    private AliyunConfig aliyun = new AliyunConfig();
+    private ThumbImage thumbImage = new ThumbImage();
+    private MinioConfig minio = new MinioConfig();
+    private QiniuyunConfig qiniuyun = new QiniuyunConfig();
+}

+ 11 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/config/AliyunConfig.java

@@ -0,0 +1,11 @@
+package com.qiwenshare.ufop.config;
+
+import com.qiwenshare.ufop.domain.AliyunOSS;
+import lombok.Data;
+
+@Data
+public class  AliyunConfig {
+    private AliyunOSS oss = new AliyunOSS();
+
+
+}

+ 11 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/config/MinioConfig.java

@@ -0,0 +1,11 @@
+package com.qiwenshare.ufop.config;
+
+import lombok.Data;
+
+@Data
+public class MinioConfig {
+    private String endpoint;
+    private String accessKey;
+    private String secretKey;
+    private String bucketName;
+}

+ 9 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/config/QiniuyunConfig.java

@@ -0,0 +1,9 @@
+package com.qiwenshare.ufop.config;
+
+import com.qiwenshare.ufop.domain.QiniuyunKodo;
+import lombok.Data;
+
+@Data
+public class QiniuyunConfig {
+    private QiniuyunKodo kodo = new QiniuyunKodo();
+}

+ 27 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/constant/FilePermissionEnum.java

@@ -0,0 +1,27 @@
+package com.qiwenshare.ufop.constant;
+
+/**
+ * @author MAC
+ * @version 1.0
+ */
+public enum FilePermissionEnum {
+    NO("0", "无权限"),
+    READ("1", "读取"),
+    READ_WRITE("2", "读取/写入"),
+    OWNER("3", "所有者");
+
+    private final String type;
+    private final String desc;
+    FilePermissionEnum(String type, String desc) {
+        this.type = type;
+        this.desc = desc;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public String getDesc() {
+        return desc;
+    }
+}

+ 27 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/constant/StorageTypeEnum.java

@@ -0,0 +1,27 @@
+package com.qiwenshare.ufop.constant;
+
+
+public enum StorageTypeEnum {
+    LOCAL(0, "本地存储"),
+    ALIYUN_OSS(1, "阿里云OSS对象存储"),
+    FAST_DFS(2, "fastDFS集群存储"),
+    MINIO(3, "minio存储"),
+    QINIUYUN_KODO(4, "七牛云KODO对象存储");
+    private final int code;
+    private final String name;
+
+    StorageTypeEnum(int code, String name) {
+        this.code = code;
+        this.name = name;
+    }
+
+    public int getCode() {
+        return code;
+    }
+
+
+    public String getName() {
+        return name;
+    }
+
+}

+ 25 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/constant/UploadFileStatusEnum.java

@@ -0,0 +1,25 @@
+package com.qiwenshare.ufop.constant;
+
+public enum UploadFileStatusEnum {
+
+    FAIL(0, "上传失败"),
+    SUCCESS(1, "上传成功"),
+    UNCOMPLATE(2, "未完成");
+
+    private final int code;
+    private final String message;
+
+    UploadFileStatusEnum(int code, String message) {
+        this.code = code;
+        this.message = message;
+    }
+
+    public int getCode() {
+        return code;
+    }
+
+    public String getMessage() {
+        return message;
+    }
+
+}

+ 13 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/domain/AliyunOSS.java

@@ -0,0 +1,13 @@
+package com.qiwenshare.ufop.domain;
+
+import lombok.Data;
+
+@Data
+public class AliyunOSS {
+
+    private String endpoint;
+    private String accessKeyId;
+    private String accessKeySecret;
+    private String bucketName;
+    private String objectName;
+}

+ 12 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/domain/QiniuyunKodo.java

@@ -0,0 +1,12 @@
+package com.qiwenshare.ufop.domain;
+
+import lombok.Data;
+
+@Data
+public class QiniuyunKodo {
+    private String domain;
+    private String endpoint;
+    private String accessKey;
+    private String secretKey;
+    private String bucketName;
+}

+ 9 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/domain/ThumbImage.java

@@ -0,0 +1,9 @@
+package com.qiwenshare.ufop.domain;
+
+import lombok.Data;
+
+@Data
+public class ThumbImage {
+    private int width;
+    private int height;
+}

+ 16 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/exception/UFOPException.java

@@ -0,0 +1,16 @@
+package com.qiwenshare.ufop.exception;
+
+public class UFOPException extends RuntimeException {
+    public UFOPException(Throwable cause) {
+        super("统一文件操作平台(UFOP)出现异常", cause);
+    }
+
+    public UFOPException(String message) {
+        super(message);
+    }
+
+    public UFOPException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+}

+ 18 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/exception/operation/CopyException.java

@@ -0,0 +1,18 @@
+package com.qiwenshare.ufop.exception.operation;
+
+import com.qiwenshare.ufop.exception.UFOPException;
+
+public class CopyException extends UFOPException {
+    public CopyException(Throwable cause) {
+        super("创建出现了异常", cause);
+    }
+
+    public CopyException(String message) {
+        super(message);
+    }
+
+    public CopyException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+}

+ 16 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/exception/operation/DeleteException.java

@@ -0,0 +1,16 @@
+package com.qiwenshare.ufop.exception.operation;
+
+public class DeleteException extends RuntimeException{
+    public DeleteException(Throwable cause) {
+        super("删除出现了异常", cause);
+    }
+
+    public DeleteException(String message) {
+        super(message);
+    }
+
+    public DeleteException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+}

+ 16 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/exception/operation/DownloadException.java

@@ -0,0 +1,16 @@
+package com.qiwenshare.ufop.exception.operation;
+
+public class DownloadException extends RuntimeException{
+    public DownloadException(Throwable cause) {
+        super("下载出现了异常", cause);
+    }
+
+    public DownloadException(String message) {
+        super(message);
+    }
+
+    public DownloadException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+}

+ 16 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/exception/operation/PreviewException.java

@@ -0,0 +1,16 @@
+package com.qiwenshare.ufop.exception.operation;
+
+public class PreviewException extends RuntimeException {
+    public PreviewException(Throwable cause) {
+        super("预览出现了异常", cause);
+    }
+
+    public PreviewException(String message) {
+        super(message);
+    }
+
+    public PreviewException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+}

+ 15 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/exception/operation/ReadException.java

@@ -0,0 +1,15 @@
+package com.qiwenshare.ufop.exception.operation;
+
+public class ReadException extends RuntimeException{
+    public ReadException(Throwable cause) {
+        super("文件读取出现了异常", cause);
+    }
+
+    public ReadException(String message) {
+        super(message);
+    }
+
+    public ReadException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}

+ 16 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/exception/operation/UploadException.java

@@ -0,0 +1,16 @@
+package com.qiwenshare.ufop.exception.operation;
+
+public class UploadException extends RuntimeException{
+    public UploadException(Throwable cause) {
+        super("上传出现了异常", cause);
+    }
+
+    public UploadException(String message) {
+        super(message);
+    }
+
+    public UploadException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+}

+ 15 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/exception/operation/WriteException.java

@@ -0,0 +1,15 @@
+package com.qiwenshare.ufop.exception.operation;
+
+public class WriteException extends RuntimeException{
+    public WriteException(Throwable cause) {
+        super("文件写入出现了异常", cause);
+    }
+
+    public WriteException(String message) {
+        super(message);
+    }
+
+    public WriteException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}

+ 180 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/factory/UFOPFactory.java

@@ -0,0 +1,180 @@
+package com.qiwenshare.ufop.factory;
+
+import com.qiwenshare.ufop.autoconfiguration.UFOPProperties;
+import com.qiwenshare.ufop.config.AliyunConfig;
+import com.qiwenshare.ufop.config.MinioConfig;
+import com.qiwenshare.ufop.config.QiniuyunConfig;
+import com.qiwenshare.ufop.constant.StorageTypeEnum;
+import com.qiwenshare.ufop.domain.ThumbImage;
+import com.qiwenshare.ufop.operation.copy.Copier;
+import com.qiwenshare.ufop.operation.copy.product.*;
+import com.qiwenshare.ufop.operation.delete.Deleter;
+import com.qiwenshare.ufop.operation.delete.product.*;
+import com.qiwenshare.ufop.operation.download.Downloader;
+import com.qiwenshare.ufop.operation.download.product.*;
+import com.qiwenshare.ufop.operation.preview.Previewer;
+import com.qiwenshare.ufop.operation.preview.product.*;
+import com.qiwenshare.ufop.operation.read.Reader;
+import com.qiwenshare.ufop.operation.read.product.*;
+import com.qiwenshare.ufop.operation.upload.Uploader;
+import com.qiwenshare.ufop.operation.upload.product.*;
+import com.qiwenshare.ufop.operation.write.Writer;
+import com.qiwenshare.ufop.operation.write.product.*;
+
+import javax.annotation.Resource;
+
+public class UFOPFactory {
+    private String storageType;
+    private AliyunConfig aliyunConfig;
+    private ThumbImage thumbImage;
+    private MinioConfig minioConfig;
+    private QiniuyunConfig qiniuyunConfig;
+    @Resource
+    private FastDFSCopier fastDFSCopier;
+    @Resource
+    private FastDFSUploader fastDFSUploader;
+    @Resource
+    private FastDFSDownloader fastDFSDownloader;
+    @Resource
+    private  FastDFSDeleter fastDFSDeleter;
+    @Resource
+    private FastDFSReader fastDFSReader;
+    @Resource
+    private FastDFSPreviewer fastDFSPreviewer;
+    @Resource
+    private FastDFSWriter fastDFSWriter;
+    @Resource
+    private AliyunOSSUploader aliyunOSSUploader;
+    @Resource
+    private MinioUploader minioUploader;
+    @Resource
+    private QiniuyunKodoUploader qiniuyunKodoUploader;
+
+    public UFOPFactory() {
+    }
+
+    public UFOPFactory(UFOPProperties ufopProperties) {
+        this.storageType = ufopProperties.getStorageType();
+        this.aliyunConfig = ufopProperties.getAliyun();
+        this.thumbImage = ufopProperties.getThumbImage();
+        this.minioConfig = ufopProperties.getMinio();
+        this.qiniuyunConfig = ufopProperties.getQiniuyun();
+    }
+
+    public Uploader getUploader() {
+
+        int type = Integer.parseInt(storageType);
+        Uploader uploader = null;
+        if (StorageTypeEnum.LOCAL.getCode() == type) {
+            uploader = new LocalStorageUploader();
+        } else if (StorageTypeEnum.ALIYUN_OSS.getCode() == type) {
+            uploader = aliyunOSSUploader;
+        } else if (StorageTypeEnum.FAST_DFS.getCode() == type) {
+            uploader = fastDFSUploader;
+        } else if (StorageTypeEnum.MINIO.getCode() == type) {
+            uploader = minioUploader;
+        } else if (StorageTypeEnum.QINIUYUN_KODO.getCode() == type) {
+            uploader = qiniuyunKodoUploader;
+        }
+        return uploader;
+    }
+
+
+    public Downloader getDownloader(int storageType) {
+        Downloader downloader = null;
+        if (StorageTypeEnum.LOCAL.getCode() == storageType) {
+            downloader = new LocalStorageDownloader();
+        } else if (StorageTypeEnum.ALIYUN_OSS.getCode() == storageType) {
+            downloader = new AliyunOSSDownloader(aliyunConfig);
+        } else if (StorageTypeEnum.FAST_DFS.getCode() == storageType) {
+            downloader = fastDFSDownloader;
+        } else if (StorageTypeEnum.MINIO.getCode() == storageType) {
+            downloader = new MinioDownloader(minioConfig);
+        } else if (StorageTypeEnum.QINIUYUN_KODO.getCode() == storageType) {
+            downloader = new QiniuyunKodoDownloader(qiniuyunConfig);
+        }
+        return downloader;
+    }
+
+
+    public Deleter getDeleter(int storageType) {
+        Deleter deleter = null;
+        if (StorageTypeEnum.LOCAL.getCode() == storageType) {
+            deleter = new LocalStorageDeleter();
+        } else if (StorageTypeEnum.ALIYUN_OSS.getCode() == storageType) {
+            deleter = new AliyunOSSDeleter(aliyunConfig);
+        } else if (StorageTypeEnum.FAST_DFS.getCode() == storageType) {
+            deleter = fastDFSDeleter;
+        } else if (StorageTypeEnum.MINIO.getCode() == storageType) {
+            deleter = new MinioDeleter(minioConfig);
+        } else if (StorageTypeEnum.QINIUYUN_KODO.getCode() == storageType) {
+            deleter = new QiniuyunKodoDeleter(qiniuyunConfig);
+        }
+        return deleter;
+    }
+
+    public Reader getReader(int storageType) {
+        Reader reader = null;
+        if (StorageTypeEnum.LOCAL.getCode() == storageType) {
+            reader = new LocalStorageReader();
+        } else if (StorageTypeEnum.ALIYUN_OSS.getCode() == storageType) {
+            reader = new AliyunOSSReader(aliyunConfig);
+        } else if (StorageTypeEnum.FAST_DFS.getCode() == storageType) {
+            reader = fastDFSReader;
+        } else if (StorageTypeEnum.MINIO.getCode() == storageType) {
+            reader = new MinioReader(minioConfig);
+        } else if (StorageTypeEnum.QINIUYUN_KODO.getCode() == storageType) {
+            reader = new QiniuyunKodoReader(qiniuyunConfig);
+        }
+        return reader;
+    }
+
+    public Writer getWriter(int storageType) {
+        Writer writer = null;
+        if (StorageTypeEnum.LOCAL.getCode() == storageType) {
+            writer = new LocalStorageWriter();
+        } else if (StorageTypeEnum.ALIYUN_OSS.getCode() == storageType) {
+            writer = new AliyunOSSWriter(aliyunConfig);
+        } else if (StorageTypeEnum.FAST_DFS.getCode() == storageType) {
+            writer = fastDFSWriter;
+        } else if (StorageTypeEnum.MINIO.getCode() == storageType) {
+            writer = new MinioWriter(minioConfig);
+        } else if (StorageTypeEnum.QINIUYUN_KODO.getCode() == storageType) {
+            writer = new QiniuyunKodoWriter(qiniuyunConfig);
+        }
+        return writer;
+    }
+
+    public Previewer getPreviewer(int storageType) {
+        Previewer previewer = null;
+        if (StorageTypeEnum.LOCAL.getCode() == storageType) {
+            previewer = new LocalStoragePreviewer(thumbImage);
+        } else if (StorageTypeEnum.ALIYUN_OSS.getCode() == storageType) {
+            previewer = new AliyunOSSPreviewer(aliyunConfig, thumbImage);
+        } else if (StorageTypeEnum.FAST_DFS.getCode() == storageType) {
+            previewer = fastDFSPreviewer;
+        } else if (StorageTypeEnum.MINIO.getCode() == storageType) {
+            previewer = new MinioPreviewer(minioConfig, thumbImage);
+        } else if (StorageTypeEnum.QINIUYUN_KODO.getCode() == storageType) {
+            previewer = new QiniuyunKodoPreviewer(qiniuyunConfig, thumbImage);
+        }
+        return previewer;
+    }
+
+    public Copier getCopier() {
+        int type = Integer.parseInt(storageType);
+        Copier copier = null;
+        if (StorageTypeEnum.LOCAL.getCode() == type) {
+            copier = new LocalStorageCopier();
+        } else if (StorageTypeEnum.ALIYUN_OSS.getCode() == type) {
+            copier = new AliyunOSSCopier(aliyunConfig);
+        } else if (StorageTypeEnum.FAST_DFS.getCode() == type) {
+            copier = fastDFSCopier;
+        } else if (StorageTypeEnum.MINIO.getCode() == type) {
+            copier = new MinioCopier(minioConfig);
+        } else if (StorageTypeEnum.QINIUYUN_KODO.getCode() == type) {
+            copier = new QiniuyunKodoCopier(qiniuyunConfig);
+        }
+        return copier;
+    }
+}

+ 15 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/copy/Copier.java

@@ -0,0 +1,15 @@
+package com.qiwenshare.ufop.operation.copy;
+
+import com.qiwenshare.ufop.operation.copy.domain.CopyFile;
+
+import java.io.InputStream;
+
+public abstract class Copier {
+    /**
+     * 将服务器文件流拷贝到云端,并返回文件url
+     * @param inputStream 文件流
+     * @param copyFile 拷贝文件相关参数
+     * @return 文件url
+     */
+    public abstract String copy(InputStream inputStream, CopyFile copyFile);
+}

+ 8 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/copy/domain/CopyFile.java

@@ -0,0 +1,8 @@
+package com.qiwenshare.ufop.operation.copy.domain;
+
+import lombok.Data;
+
+@Data
+public class CopyFile {
+    private String extendName;
+}

+ 39 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/copy/product/AliyunOSSCopier.java

@@ -0,0 +1,39 @@
+package com.qiwenshare.ufop.operation.copy.product;
+
+import com.aliyun.oss.OSS;
+import com.qiwenshare.ufop.config.AliyunConfig;
+import com.qiwenshare.ufop.operation.copy.Copier;
+import com.qiwenshare.ufop.operation.copy.domain.CopyFile;
+import com.qiwenshare.ufop.util.AliyunUtils;
+import com.qiwenshare.ufop.util.UFOPUtils;
+import org.apache.commons.io.IOUtils;
+
+import java.io.InputStream;
+import java.util.UUID;
+
+public class AliyunOSSCopier extends Copier {
+
+    private AliyunConfig aliyunConfig;
+
+    public AliyunOSSCopier(){
+
+    }
+
+    public AliyunOSSCopier(AliyunConfig aliyunConfig) {
+        this.aliyunConfig = aliyunConfig;
+    }
+    @Override
+    public String copy(InputStream inputStream, CopyFile copyFile) {
+        String uuid = UUID.randomUUID().toString();
+        String fileUrl = UFOPUtils.getUploadFileUrl(uuid, copyFile.getExtendName());
+        OSS ossClient = AliyunUtils.getOSSClient(aliyunConfig);
+        try {
+            ossClient.putObject(aliyunConfig.getOss().getBucketName(), fileUrl, inputStream);
+        } finally {
+            IOUtils.closeQuietly(inputStream);
+            ossClient.shutdown();
+        }
+        return fileUrl;
+    }
+
+}

+ 30 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/copy/product/FastDFSCopier.java

@@ -0,0 +1,30 @@
+package com.qiwenshare.ufop.operation.copy.product;
+
+import com.github.tobato.fastdfs.domain.StorePath;
+import com.github.tobato.fastdfs.service.AppendFileStorageClient;
+import com.qiwenshare.ufop.operation.copy.Copier;
+import com.qiwenshare.ufop.operation.copy.domain.CopyFile;
+import org.apache.commons.io.IOUtils;
+
+import javax.annotation.Resource;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class FastDFSCopier extends Copier {
+    @Resource
+    AppendFileStorageClient defaultAppendFileStorageClient;
+
+    @Override
+    public String copy(InputStream inputStream, CopyFile copyFile) {
+        StorePath storePath = new StorePath();
+        try {
+            storePath = defaultAppendFileStorageClient.uploadAppenderFile("group1", inputStream,
+                    inputStream.available(), copyFile.getExtendName());
+        } catch (IOException e) {
+            e.printStackTrace();
+        } finally {
+            IOUtils.closeQuietly(inputStream);
+        }
+        return storePath.getPath();
+    }
+}

+ 32 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/copy/product/LocalStorageCopier.java

@@ -0,0 +1,32 @@
+package com.qiwenshare.ufop.operation.copy.product;
+
+import com.qiwenshare.ufop.exception.operation.CopyException;
+import com.qiwenshare.ufop.operation.copy.Copier;
+import com.qiwenshare.ufop.operation.copy.domain.CopyFile;
+import com.qiwenshare.ufop.util.UFOPUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.UUID;
+
+@Slf4j
+public class LocalStorageCopier extends Copier {
+    @Override
+    public String copy(InputStream inputStream, CopyFile copyFile) {
+        String uuid = UUID.randomUUID().toString();
+        String fileUrl = UFOPUtils.getUploadFileUrl(uuid, copyFile.getExtendName());
+        File saveFile = new File(UFOPUtils.getStaticPath() + fileUrl);
+        try {
+            FileUtils.copyInputStreamToFile(inputStream, saveFile);
+        } catch (IOException e) {
+            throw new CopyException("创建文件出现异常", e);
+        } finally {
+            IOUtils.closeQuietly(inputStream);
+        }
+        return fileUrl;
+    }
+}

+ 65 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/copy/product/MinioCopier.java

@@ -0,0 +1,65 @@
+package com.qiwenshare.ufop.operation.copy.product;
+
+import com.qiwenshare.ufop.config.MinioConfig;
+import com.qiwenshare.ufop.operation.copy.Copier;
+import com.qiwenshare.ufop.operation.copy.domain.CopyFile;
+import com.qiwenshare.ufop.util.UFOPUtils;
+import io.minio.BucketExistsArgs;
+import io.minio.MakeBucketArgs;
+import io.minio.MinioClient;
+import io.minio.PutObjectArgs;
+import io.minio.errors.MinioException;
+import org.apache.commons.io.IOUtils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.util.UUID;
+
+public class MinioCopier extends Copier {
+
+    private MinioConfig minioConfig;
+
+    public MinioCopier(){
+
+    }
+
+    public MinioCopier(MinioConfig minioConfig) {
+        this.minioConfig = minioConfig;
+    }
+    @Override
+    public String copy(InputStream inputStream, CopyFile copyFile) {
+        String uuid = UUID.randomUUID().toString();
+        String fileUrl = UFOPUtils.getUploadFileUrl(uuid, copyFile.getExtendName());
+
+
+
+        MinioClient minioClient;
+        try {
+            minioClient =
+                    MinioClient.builder().endpoint(minioConfig.getEndpoint())
+                            .credentials(minioConfig.getAccessKey(), minioConfig.getSecretKey()).build();
+            // 检查存储桶是否已经存在
+            boolean isExist = minioClient.bucketExists(BucketExistsArgs.builder().bucket(minioConfig.getBucketName()).build());
+            if(!isExist) {
+                minioClient.makeBucket(MakeBucketArgs.builder().bucket(minioConfig.getBucketName()).build());
+            }
+
+            minioClient.putObject(
+                    PutObjectArgs.builder().bucket(minioConfig.getBucketName()).object(fileUrl).stream(
+                                    inputStream, inputStream.available(), 1024 * 1024 * 5)
+//                            .contentType("video/mp4")
+                            .build());
+
+        } catch (MinioException | InvalidKeyException | NoSuchAlgorithmException | IOException e) {
+            e.printStackTrace();
+        } finally {
+            IOUtils.closeQuietly(inputStream);
+        }
+
+        return fileUrl;
+    }
+
+
+}

+ 83 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/copy/product/QiniuyunKodoCopier.java

@@ -0,0 +1,83 @@
+package com.qiwenshare.ufop.operation.copy.product;
+
+import com.google.gson.Gson;
+import com.qiniu.common.QiniuException;
+import com.qiniu.http.Response;
+import com.qiniu.storage.Configuration;
+import com.qiniu.storage.UploadManager;
+import com.qiniu.storage.model.DefaultPutRet;
+import com.qiniu.storage.persistent.FileRecorder;
+import com.qiniu.util.Auth;
+import com.qiwenshare.ufop.config.QiniuyunConfig;
+import com.qiwenshare.ufop.operation.copy.Copier;
+import com.qiwenshare.ufop.operation.copy.domain.CopyFile;
+import com.qiwenshare.ufop.util.QiniuyunUtils;
+import com.qiwenshare.ufop.util.UFOPUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.io.IOUtils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.UUID;
+
+@Slf4j
+public class QiniuyunKodoCopier extends Copier {
+
+    private QiniuyunConfig qiniuyunConfig;
+
+    public QiniuyunKodoCopier(){
+
+    }
+
+    public QiniuyunKodoCopier(QiniuyunConfig qiniuyunConfig) {
+        this.qiniuyunConfig = qiniuyunConfig;
+    }
+    @Override
+    public String copy(InputStream inputStream, CopyFile copyFile) {
+        String uuid = UUID.randomUUID().toString();
+        String fileUrl = UFOPUtils.getUploadFileUrl(uuid, copyFile.getExtendName());
+
+        qiniuUpload(fileUrl, inputStream);
+
+        return fileUrl;
+    }
+
+    private void qiniuUpload(String fileUrl, InputStream inputStream) {
+        Configuration cfg = QiniuyunUtils.getCfg(qiniuyunConfig);
+
+
+        Auth auth = Auth.create(qiniuyunConfig.getKodo().getAccessKey(), qiniuyunConfig.getKodo().getSecretKey());
+        String upToken = auth.uploadToken(qiniuyunConfig.getKodo().getBucketName());
+
+        String localTempDir = UFOPUtils.getStaticPath() + "temp";
+
+
+        try {
+            //设置断点续传文件进度保存目录
+            FileRecorder fileRecorder = new FileRecorder(localTempDir);
+            UploadManager uploadManager = new UploadManager(cfg, fileRecorder);
+            Response response = uploadManager.put(inputStream, fileUrl, upToken, null, null);
+            //解析上传成功的结果
+            DefaultPutRet putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class);
+            log.info(putRet.key);
+            log.info(putRet.hash);
+        } catch (QiniuException ex) {
+            Response r = ex.response;
+            System.err.println(r.toString());
+            try {
+                System.err.println(r.bodyString());
+            } catch (QiniuException ex2) {
+                //ignore
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        } finally {
+            IOUtils.closeQuietly(inputStream);
+        }
+
+
+
+    }
+
+
+}

+ 25 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/delete/Deleter.java

@@ -0,0 +1,25 @@
+package com.qiwenshare.ufop.operation.delete;
+
+import com.qiwenshare.ufop.operation.delete.domain.DeleteFile;
+import com.qiwenshare.ufop.util.UFOPUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.io.FilenameUtils;
+
+import java.io.File;
+
+@Slf4j
+public abstract class Deleter {
+    public abstract void delete(DeleteFile deleteFile);
+
+    protected void deleteCacheFile(DeleteFile deleteFile) {
+        if (UFOPUtils.isImageFile(FilenameUtils.getExtension(deleteFile.getFileUrl()))) {
+            File cacheFile = UFOPUtils.getCacheFile(deleteFile.getFileUrl());
+            if (cacheFile.exists()) {
+                boolean result = cacheFile.delete();
+                if (!result) {
+                    log.error("删除本地缓存文件失败!");
+                }
+            }
+        }
+    }
+}

+ 8 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/delete/domain/DeleteFile.java

@@ -0,0 +1,8 @@
+package com.qiwenshare.ufop.operation.delete.domain;
+
+import lombok.Data;
+
+@Data
+public class DeleteFile {
+    private String fileUrl;
+}

+ 31 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/delete/product/AliyunOSSDeleter.java

@@ -0,0 +1,31 @@
+package com.qiwenshare.ufop.operation.delete.product;
+
+import com.aliyun.oss.OSS;
+import com.qiwenshare.ufop.config.AliyunConfig;
+import com.qiwenshare.ufop.operation.delete.Deleter;
+import com.qiwenshare.ufop.operation.delete.domain.DeleteFile;
+import com.qiwenshare.ufop.util.AliyunUtils;
+import com.qiwenshare.ufop.util.UFOPUtils;
+
+
+public class AliyunOSSDeleter extends Deleter {
+    private AliyunConfig aliyunConfig;
+
+    public AliyunOSSDeleter(){
+
+    }
+
+    public AliyunOSSDeleter(AliyunConfig aliyunConfig) {
+        this.aliyunConfig = aliyunConfig;
+    }
+    @Override
+    public void delete(DeleteFile deleteFile) {
+        OSS ossClient = AliyunUtils.getOSSClient(aliyunConfig);
+        try {
+            ossClient.deleteObject(aliyunConfig.getOss().getBucketName(), UFOPUtils.getAliyunObjectNameByFileUrl(deleteFile.getFileUrl()));
+        } finally {
+            ossClient.shutdown();
+        }
+        deleteCacheFile(deleteFile);
+    }
+}

+ 25 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/delete/product/FastDFSDeleter.java

@@ -0,0 +1,25 @@
+package com.qiwenshare.ufop.operation.delete.product;
+
+import com.github.tobato.fastdfs.exception.FdfsServerException;
+import com.github.tobato.fastdfs.service.FastFileStorageClient;
+import com.qiwenshare.ufop.operation.delete.Deleter;
+import com.qiwenshare.ufop.operation.delete.domain.DeleteFile;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Slf4j
+@Component
+public class FastDFSDeleter extends Deleter {
+    @Autowired
+    private FastFileStorageClient fastFileStorageClient;
+    @Override
+    public void delete(DeleteFile deleteFile) {
+        try {
+            fastFileStorageClient.deleteFile(deleteFile.getFileUrl().replace("M00", "group1"));
+        } catch (FdfsServerException e) {
+            log.error(e.getMessage());
+        }
+        deleteCacheFile(deleteFile);
+    }
+}

+ 25 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/delete/product/LocalStorageDeleter.java

@@ -0,0 +1,25 @@
+package com.qiwenshare.ufop.operation.delete.product;
+
+import com.qiwenshare.ufop.exception.operation.DeleteException;
+import com.qiwenshare.ufop.operation.delete.Deleter;
+import com.qiwenshare.ufop.operation.delete.domain.DeleteFile;
+import com.qiwenshare.ufop.util.UFOPUtils;
+import org.springframework.stereotype.Component;
+
+import java.io.File;
+
+@Component
+public class LocalStorageDeleter extends Deleter {
+    @Override
+    public void delete(DeleteFile deleteFile) {
+        File localSaveFile = UFOPUtils.getLocalSaveFile(deleteFile.getFileUrl());
+        if (localSaveFile.exists()) {
+            boolean result = localSaveFile.delete();
+            if (!result) {
+                throw new DeleteException("删除本地文件失败");
+            }
+        }
+
+        deleteCacheFile(deleteFile);
+    }
+}

+ 47 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/delete/product/MinioDeleter.java

@@ -0,0 +1,47 @@
+package com.qiwenshare.ufop.operation.delete.product;
+
+import com.qiwenshare.ufop.config.MinioConfig;
+import com.qiwenshare.ufop.exception.operation.DeleteException;
+import com.qiwenshare.ufop.operation.delete.Deleter;
+import com.qiwenshare.ufop.operation.delete.domain.DeleteFile;
+import io.minio.MinioClient;
+import io.minio.RemoveObjectArgs;
+import io.minio.errors.*;
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.IOException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+
+
+@Slf4j
+public class MinioDeleter extends Deleter {
+    private MinioConfig minioConfig;
+
+    public MinioDeleter(){
+
+    }
+
+    public MinioDeleter(MinioConfig minioConfig) {
+        this.minioConfig = minioConfig;
+    }
+    @Override
+    public void delete(DeleteFile deleteFile) {
+
+        try {
+            MinioClient minioClient =
+                    MinioClient.builder().endpoint(minioConfig.getEndpoint())
+                            .credentials(minioConfig.getAccessKey(), minioConfig.getSecretKey()).build();
+            // 从mybucket中删除myobject。
+            minioClient.removeObject(RemoveObjectArgs.builder().bucket(minioConfig.getBucketName()).object(deleteFile.getFileUrl()).build());
+            log.info("successfully removed mybucket/myobject");
+        } catch (MinioException e) {
+            log.error("Error: " + e);
+            throw new DeleteException("Minio删除文件失败", e);
+        } catch (IOException | InvalidKeyException | NoSuchAlgorithmException e) {
+            throw new DeleteException("Minio删除文件失败", e);
+        }
+        deleteCacheFile(deleteFile);
+
+    }
+}

+ 39 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/delete/product/QiniuyunKodoDeleter.java

@@ -0,0 +1,39 @@
+package com.qiwenshare.ufop.operation.delete.product;
+
+import com.qiniu.common.QiniuException;
+import com.qiniu.storage.BucketManager;
+import com.qiniu.storage.Configuration;
+import com.qiniu.util.Auth;
+import com.qiwenshare.ufop.config.QiniuyunConfig;
+import com.qiwenshare.ufop.exception.operation.DeleteException;
+import com.qiwenshare.ufop.operation.delete.Deleter;
+import com.qiwenshare.ufop.operation.delete.domain.DeleteFile;
+import com.qiwenshare.ufop.util.QiniuyunUtils;
+import lombok.extern.slf4j.Slf4j;
+
+
+@Slf4j
+public class QiniuyunKodoDeleter extends Deleter {
+    private QiniuyunConfig qiniuyunConfig;
+
+    public QiniuyunKodoDeleter(){
+
+    }
+
+    public QiniuyunKodoDeleter(QiniuyunConfig qiniuyunConfig) {
+        this.qiniuyunConfig = qiniuyunConfig;
+    }
+    @Override
+    public void delete(DeleteFile deleteFile) {
+        Configuration cfg = QiniuyunUtils.getCfg(qiniuyunConfig);
+        Auth auth = Auth.create(qiniuyunConfig.getKodo().getAccessKey(), qiniuyunConfig.getKodo().getSecretKey());
+        BucketManager bucketManager = new BucketManager(auth, cfg);
+        try {
+            bucketManager.delete(qiniuyunConfig.getKodo().getBucketName(), deleteFile.getFileUrl());
+        } catch (QiniuException ex) {
+            throw new DeleteException("七牛云删除文件失败", ex);
+        }
+        deleteCacheFile(deleteFile);
+
+    }
+}

+ 34 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/download/Downloader.java

@@ -0,0 +1,34 @@
+package com.qiwenshare.ufop.operation.download;
+
+import com.aliyun.oss.OSS;
+import com.qiwenshare.ufop.operation.download.domain.DownloadFile;
+import org.apache.commons.io.IOUtils;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+public abstract class Downloader {
+
+    public void download(HttpServletResponse httpServletResponse, DownloadFile downloadFile) {
+
+        InputStream inputStream = getInputStream(downloadFile);
+        OutputStream outputStream = null;
+        try {
+            outputStream = httpServletResponse.getOutputStream();
+            IOUtils.copyLarge(inputStream, outputStream);
+        } catch (IOException e) {
+            e.printStackTrace();
+        } finally {
+            IOUtils.closeQuietly(inputStream);
+            IOUtils.closeQuietly(outputStream);
+            OSS ossClient = downloadFile.getOssClient();
+            if (ossClient != null) {
+                ossClient.shutdown();
+            }
+        }
+
+    }
+    public abstract InputStream getInputStream(DownloadFile downloadFile);
+}

+ 11 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/download/domain/DownloadFile.java

@@ -0,0 +1,11 @@
+package com.qiwenshare.ufop.operation.download.domain;
+
+import com.aliyun.oss.OSS;
+import lombok.Data;
+
+@Data
+public class DownloadFile {
+    private String fileUrl;
+    private OSS ossClient;
+    private Range range;
+}

+ 13 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/download/domain/Range.java

@@ -0,0 +1,13 @@
+package com.qiwenshare.ufop.operation.download.domain;
+
+import lombok.Data;
+
+/**
+ * @author MAC
+ * @version 1.0
+ */
+@Data
+public class Range {
+    private long start;
+    private int length;
+}

+ 51 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/download/product/AliyunOSSDownloader.java

@@ -0,0 +1,51 @@
+package com.qiwenshare.ufop.operation.download.product;
+
+import com.aliyun.oss.OSS;
+import com.aliyun.oss.model.GetObjectRequest;
+import com.aliyun.oss.model.OSSObject;
+import com.qiwenshare.ufop.config.AliyunConfig;
+import com.qiwenshare.ufop.operation.download.Downloader;
+import com.qiwenshare.ufop.operation.download.domain.DownloadFile;
+import com.qiwenshare.ufop.util.AliyunUtils;
+import com.qiwenshare.ufop.util.UFOPUtils;
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.*;
+
+@Slf4j
+public class AliyunOSSDownloader extends Downloader {
+
+    private AliyunConfig aliyunConfig;
+
+    public AliyunOSSDownloader(){
+
+    }
+
+    public AliyunOSSDownloader(AliyunConfig aliyunConfig) {
+        this.aliyunConfig = aliyunConfig;
+    }
+
+    @Override
+    public InputStream getInputStream(DownloadFile downloadFile) {
+
+        OSS ossClient = AliyunUtils.getOSSClient(aliyunConfig);
+        OSSObject ossObject;
+        if (downloadFile.getRange() != null) {
+            GetObjectRequest getObjectRequest = new GetObjectRequest(aliyunConfig.getOss().getBucketName(),
+                    UFOPUtils.getAliyunObjectNameByFileUrl(downloadFile.getFileUrl()));
+            getObjectRequest.setRange(downloadFile.getRange().getStart(),
+                    downloadFile.getRange().getStart() + downloadFile.getRange().getLength() - 1);
+            ossObject = ossClient.getObject(getObjectRequest);
+        } else {
+            ossObject = ossClient.getObject(aliyunConfig.getOss().getBucketName(),
+                    UFOPUtils.getAliyunObjectNameByFileUrl(downloadFile.getFileUrl()));
+        }
+
+        InputStream inputStream = ossObject.getObjectContent();
+
+        downloadFile.setOssClient(ossClient);
+        return inputStream;
+    }
+
+
+}

+ 37 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/download/product/FastDFSDownloader.java

@@ -0,0 +1,37 @@
+package com.qiwenshare.ufop.operation.download.product;
+
+import com.github.tobato.fastdfs.proto.storage.DownloadByteArray;
+import com.github.tobato.fastdfs.service.FastFileStorageClient;
+import com.qiwenshare.ufop.operation.download.Downloader;
+import com.qiwenshare.ufop.operation.download.domain.DownloadFile;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+
+@Slf4j
+@Component
+public class FastDFSDownloader extends Downloader {
+    @Autowired
+    private FastFileStorageClient fastFileStorageClient;
+
+    @Override
+    public InputStream getInputStream(DownloadFile downloadFile) {
+        String group;
+        group = "group1";
+        String path = downloadFile.getFileUrl().substring(downloadFile.getFileUrl().indexOf("/") + 1);
+        DownloadByteArray downloadByteArray = new DownloadByteArray();
+        byte[] bytes;
+        if (downloadFile.getRange() != null) {
+            bytes = fastFileStorageClient.downloadFile(group, path,
+                    downloadFile.getRange().getStart(),
+                    downloadFile.getRange().getLength(),
+                    downloadByteArray);
+        } else {
+            bytes = fastFileStorageClient.downloadFile(group, path, downloadByteArray);
+        }
+        return new ByteArrayInputStream(bytes);
+    }
+}

+ 43 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/download/product/LocalStorageDownloader.java

@@ -0,0 +1,43 @@
+package com.qiwenshare.ufop.operation.download.product;
+
+import com.qiwenshare.ufop.operation.download.Downloader;
+import com.qiwenshare.ufop.operation.download.domain.DownloadFile;
+import com.qiwenshare.ufop.util.UFOPUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.io.IOUtils;
+import org.springframework.stereotype.Component;
+
+import java.io.*;
+
+@Slf4j
+@Component
+public class LocalStorageDownloader extends Downloader {
+
+    @Override
+    public InputStream getInputStream(DownloadFile downloadFile) {
+        //设置文件路径
+        File file = new File(UFOPUtils.getStaticPath() + downloadFile.getFileUrl());
+
+        InputStream inputStream = null;
+        byte[] bytes = new byte[0];
+        RandomAccessFile randowAccessFile = null;
+        try {
+            if (downloadFile.getRange() != null) {
+                randowAccessFile = new RandomAccessFile(file, "r");
+                randowAccessFile.seek(downloadFile.getRange().getStart());
+                bytes = new byte[downloadFile.getRange().getLength()];
+                randowAccessFile.read(bytes);
+            } else {
+                inputStream = new FileInputStream(file);
+                bytes = IOUtils.toByteArray(inputStream);
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        } finally {
+            IOUtils.closeQuietly(inputStream);
+            IOUtils.closeQuietly(randowAccessFile);
+        }
+        return new ByteArrayInputStream(bytes);
+
+    }
+}

+ 63 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/download/product/MinioDownloader.java

@@ -0,0 +1,63 @@
+package com.qiwenshare.ufop.operation.download.product;
+
+import com.qiwenshare.ufop.config.MinioConfig;
+import com.qiwenshare.ufop.operation.download.Downloader;
+import com.qiwenshare.ufop.operation.download.domain.DownloadFile;
+import io.minio.GetObjectArgs;
+import io.minio.MinioClient;
+import io.minio.errors.MinioException;
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+
+@Slf4j
+public class MinioDownloader extends Downloader {
+
+    private MinioConfig minioConfig;
+
+    public MinioDownloader(){
+
+    }
+
+    public MinioDownloader(MinioConfig minioConfig) {
+        this.minioConfig = minioConfig;
+    }
+
+    @Override
+    public InputStream getInputStream(DownloadFile downloadFile) {
+        InputStream inputStream = null;
+        try {
+
+            MinioClient minioClient =
+                    MinioClient.builder().endpoint(minioConfig.getEndpoint())
+                            .credentials(minioConfig.getAccessKey(), minioConfig.getSecretKey()).build();
+
+            if (downloadFile.getRange() != null) {
+                inputStream = minioClient.getObject(GetObjectArgs.builder()
+                        .bucket(minioConfig.getBucketName())
+                        .object(downloadFile.getFileUrl())
+                        .offset(downloadFile.getRange().getStart())
+                        .length((long) downloadFile.getRange().getLength())
+                        .build());
+            } else {
+                inputStream = minioClient.getObject(GetObjectArgs.builder()
+                        .bucket(minioConfig.getBucketName())
+                        .object(downloadFile.getFileUrl())
+                        .build());
+            }
+
+
+        } catch (MinioException e) {
+            System.out.println("Error occurred: " + e);
+        } catch (IOException | NoSuchAlgorithmException | InvalidKeyException e) {
+            log.error(e.getMessage());
+        }
+
+
+        return inputStream;
+    }
+
+}

+ 48 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/download/product/QiniuyunKodoDownloader.java

@@ -0,0 +1,48 @@
+package com.qiwenshare.ufop.operation.download.product;
+
+import com.qiniu.util.Auth;
+import com.qiwenshare.common.util.HttpsUtils;
+import com.qiwenshare.ufop.config.QiniuyunConfig;
+import com.qiwenshare.ufop.operation.download.Downloader;
+import com.qiwenshare.ufop.operation.download.domain.DownloadFile;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.io.IOUtils;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+@Slf4j
+public class QiniuyunKodoDownloader extends Downloader {
+
+    private QiniuyunConfig qiniuyunConfig;
+
+    public QiniuyunKodoDownloader() {
+
+    }
+
+    public QiniuyunKodoDownloader(QiniuyunConfig qiniuyunConfig) {
+        this.qiniuyunConfig = qiniuyunConfig;
+    }
+
+    @Override
+    public InputStream getInputStream(DownloadFile downloadFile) {
+        Auth auth = Auth.create(qiniuyunConfig.getKodo().getAccessKey(), qiniuyunConfig.getKodo().getSecretKey());
+
+        String urlString = auth.privateDownloadUrl(qiniuyunConfig.getKodo().getDomain() + "/" + downloadFile.getFileUrl());
+
+        InputStream inputStream = HttpsUtils.doGet(urlString, null);
+        try {
+            if (downloadFile.getRange() != null) {
+                inputStream.skip(downloadFile.getRange().getStart());
+                byte[] bytes = new byte[downloadFile.getRange().getLength()];
+                IOUtils.read(inputStream, bytes);
+                inputStream = new ByteArrayInputStream(bytes);
+            }
+        } catch (IOException e) {
+            log.error(e.getMessage());
+        }
+        return inputStream;
+    }
+
+}

+ 184 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/preview/Previewer.java

@@ -0,0 +1,184 @@
+package com.qiwenshare.ufop.operation.preview;
+
+import cn.hutool.http.HttpUtil;
+import com.qiwenshare.common.operation.ImageOperation;
+import com.qiwenshare.ufop.domain.ThumbImage;
+import com.qiwenshare.ufop.exception.operation.PreviewException;
+import com.qiwenshare.ufop.operation.preview.domain.PreviewFile;
+import com.qiwenshare.ufop.util.CharsetUtils;
+import com.qiwenshare.ufop.util.UFOPUtils;
+import lombok.Data;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.io.IOUtils;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.*;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+@Slf4j
+@Data
+public abstract class Previewer {
+
+    public ThumbImage thumbImage;
+
+    protected abstract InputStream getInputStream(PreviewFile previewFile);
+
+    public void imageThumbnailPreview(HttpServletResponse httpServletResponse, PreviewFile previewFile) {
+        String fileUrl = previewFile.getFileUrl();
+
+        if(fileUrl.startsWith("http://") || fileUrl.startsWith("https://")) {
+            String[] arr = fileUrl.replace("http://", "").replace("https://", "").split("/");
+            String name = arr[0];
+            String icoUrl = findIco(fileUrl);
+
+            File cacheFile = UFOPUtils.getCacheFile(UFOPUtils.getUploadFileUrl(name, "ico"));
+            if (cacheFile.exists()) {
+                FileInputStream fis = null;
+                OutputStream outputStream = null;
+                try {
+                    fis = new FileInputStream(cacheFile);
+                    outputStream = httpServletResponse.getOutputStream();
+                    IOUtils.copy(fis, outputStream);
+                } catch (IOException e) {
+                    e.printStackTrace();
+                } finally {
+                    IOUtils.closeQuietly(fis);
+                    IOUtils.closeQuietly(outputStream);
+                }
+            } else {
+                URL url = null;
+                InputStream in = null;
+                OutputStream outputStream = null;
+                try {
+                    url = new URL(icoUrl);
+                    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+                    InputStream inputstream = connection.getInputStream();
+                    try {
+                        outputStream = httpServletResponse.getOutputStream();
+                        in = ImageOperation.thumbnailsImageForScale(inputstream, cacheFile, 50);
+                        IOUtils.copy(in, outputStream);
+
+                    } catch (IOException e) {
+                        e.printStackTrace();
+                    } finally {
+                        IOUtils.closeQuietly(in);
+                        IOUtils.closeQuietly(inputstream);
+                        IOUtils.closeQuietly(outputStream);
+                        if (previewFile.getOssClient() != null) {
+                            previewFile.getOssClient().shutdown();
+                        }
+                    }
+                } catch (MalformedURLException e) {
+
+                    log.error("MalformedURLException, url is {}", icoUrl );
+                    throw new RuntimeException(e);
+                } catch (IOException e) {
+                    log.error("IOException, url is {}", icoUrl );
+                    throw new RuntimeException(e);
+                }
+            }
+
+            return ;
+        }
+
+
+        boolean isVideo = UFOPUtils.isVideoFile(FilenameUtils.getExtension(fileUrl));
+        String thumbnailImgUrl = previewFile.getFileUrl();
+        if (isVideo) {
+            thumbnailImgUrl = fileUrl.replace("." + FilenameUtils.getExtension(fileUrl), ".jpg");
+        }
+
+
+        File cacheFile = UFOPUtils.getCacheFile(thumbnailImgUrl);
+
+        if (cacheFile.exists()) {
+            FileInputStream fis = null;
+            OutputStream outputStream = null;
+            try {
+                fis = new FileInputStream(cacheFile);
+                outputStream = httpServletResponse.getOutputStream();
+                IOUtils.copy(fis, outputStream);
+            } catch (IOException e) {
+                e.printStackTrace();
+            } finally {
+                IOUtils.closeQuietly(fis);
+                IOUtils.closeQuietly(outputStream);
+            }
+
+        } else {
+            OutputStream outputStream = null;
+            InputStream in = null;
+            InputStream inputstream = null;
+            try {
+                inputstream = getInputStream(previewFile);
+            } catch (PreviewException previewException) {
+                log.error(previewException.getMessage());
+                return;
+            }
+
+            try {
+                outputStream = httpServletResponse.getOutputStream();
+                in = ImageOperation.thumbnailsImageForScale(inputstream, cacheFile, 50);
+                IOUtils.copy(in, outputStream);
+
+            } catch (IOException e) {
+                e.printStackTrace();
+            } finally {
+                IOUtils.closeQuietly(in);
+                IOUtils.closeQuietly(inputstream);
+                IOUtils.closeQuietly(outputStream);
+                if (previewFile.getOssClient() != null) {
+                    previewFile.getOssClient().shutdown();
+                }
+            }
+
+
+        }
+    }
+
+    public void imageOriginalPreview(HttpServletResponse httpServletResponse, PreviewFile previewFile) {
+
+        InputStream inputStream = null;
+
+        OutputStream outputStream = null;
+
+        try {
+            inputStream = getInputStream(previewFile);
+            outputStream = httpServletResponse.getOutputStream();
+            byte[] bytes = IOUtils.toByteArray(inputStream);
+            bytes = CharsetUtils.convertTxtCharsetToUTF8(bytes, FilenameUtils.getExtension(previewFile.getFileUrl()));
+            outputStream.write(bytes);
+
+        } catch (IOException e) {
+            e.printStackTrace();
+        } finally {
+            IOUtils.closeQuietly(inputStream);
+            IOUtils.closeQuietly(outputStream);
+            if (previewFile.getOssClient() != null) {
+                previewFile.getOssClient().shutdown();
+            }
+        }
+    }
+
+    private static String findIco(String navUrl) {
+        String body = HttpUtil.createGet(navUrl).execute().toString();
+        String str = body.split("favicon\\d{0,3}.ico")[0];
+        int http = str.indexOf("https://",str.length()-100);
+        if(http==-1){
+            http = str.indexOf("http://",str.length()-100);
+        }
+        if(http==-1){
+            //说明没有指定 走拼接逻辑
+            int i = navUrl.indexOf("/",8);//获取网址 拼接 favicon.ico
+            if(i>0){
+                navUrl = navUrl.substring(0, i);
+            }
+        }else {
+            navUrl = str.substring(http);
+        }
+        return navUrl+"/favicon.ico";
+    }
+}

+ 10 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/preview/domain/PreviewFile.java

@@ -0,0 +1,10 @@
+package com.qiwenshare.ufop.operation.preview.domain;
+
+import com.aliyun.oss.OSS;
+import lombok.Data;
+
+@Data
+public class PreviewFile {
+    private String fileUrl;
+    private OSS ossClient;
+}

+ 45 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/preview/product/AliyunOSSPreviewer.java

@@ -0,0 +1,45 @@
+package com.qiwenshare.ufop.operation.preview.product;
+
+import com.aliyun.oss.OSS;
+import com.aliyun.oss.model.OSSObject;
+import com.qiwenshare.ufop.config.AliyunConfig;
+import com.qiwenshare.ufop.domain.ThumbImage;
+import com.qiwenshare.ufop.operation.preview.Previewer;
+import com.qiwenshare.ufop.operation.preview.domain.PreviewFile;
+import com.qiwenshare.ufop.util.AliyunUtils;
+import com.qiwenshare.ufop.util.UFOPUtils;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.*;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+@Slf4j
+public class AliyunOSSPreviewer extends Previewer {
+
+
+    private AliyunConfig aliyunConfig;
+
+    public AliyunOSSPreviewer(){
+
+    }
+
+    public AliyunOSSPreviewer(AliyunConfig aliyunConfig, ThumbImage thumbImage) {
+        this.aliyunConfig = aliyunConfig;
+        setThumbImage(thumbImage);
+    }
+
+
+    @Override
+    protected InputStream getInputStream(PreviewFile previewFile) {
+        OSS ossClient = AliyunUtils.getOSSClient(aliyunConfig);
+        OSSObject ossObject = ossClient.getObject(aliyunConfig.getOss().getBucketName(),
+                UFOPUtils.getAliyunObjectNameByFileUrl(previewFile.getFileUrl()));
+        InputStream inputStream = ossObject.getObjectContent();
+        previewFile.setOssClient(ossClient);
+        return inputStream;
+    }
+
+}

+ 37 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/preview/product/FastDFSPreviewer.java

@@ -0,0 +1,37 @@
+package com.qiwenshare.ufop.operation.preview.product;
+
+import com.github.tobato.fastdfs.proto.storage.DownloadByteArray;
+import com.github.tobato.fastdfs.service.FastFileStorageClient;
+import com.qiwenshare.ufop.domain.ThumbImage;
+import com.qiwenshare.ufop.operation.preview.Previewer;
+import com.qiwenshare.ufop.operation.preview.domain.PreviewFile;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+
+@Slf4j
+@Component
+public class FastDFSPreviewer extends Previewer {
+
+    @Autowired
+    private FastFileStorageClient fastFileStorageClient;
+
+    public FastDFSPreviewer(){}
+
+    public FastDFSPreviewer(ThumbImage thumbImage) {
+
+        setThumbImage(thumbImage);
+    }
+
+    protected InputStream getInputStream(PreviewFile previewFile) {
+        String group = "group1";
+        String path = previewFile.getFileUrl().substring(previewFile.getFileUrl().indexOf("/") + 1);
+        DownloadByteArray downloadByteArray = new DownloadByteArray();
+        byte[] bytes = fastFileStorageClient.downloadFile(group, path, downloadByteArray);
+        return new ByteArrayInputStream(bytes);
+    }
+
+}

+ 47 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/preview/product/LocalStoragePreviewer.java

@@ -0,0 +1,47 @@
+package com.qiwenshare.ufop.operation.preview.product;
+
+import com.qiwenshare.ufop.domain.ThumbImage;
+import com.qiwenshare.ufop.exception.operation.PreviewException;
+import com.qiwenshare.ufop.operation.preview.Previewer;
+import com.qiwenshare.ufop.operation.preview.domain.PreviewFile;
+import com.qiwenshare.ufop.util.UFOPUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+
+import java.io.*;
+
+@Slf4j
+public class LocalStoragePreviewer extends Previewer {
+
+    public LocalStoragePreviewer(){
+
+    }
+    public LocalStoragePreviewer(ThumbImage thumbImage) {
+        setThumbImage(thumbImage);
+    }
+
+    @Override
+    protected InputStream getInputStream(PreviewFile previewFile) {
+        //设置文件路径
+        File file = UFOPUtils.getLocalSaveFile(previewFile.getFileUrl());
+        if (!file.exists()) {
+            throw new PreviewException("[UFOP] Failed to get the file stream because the file path does not exist! The file path is: "+ file.getAbsolutePath());
+        }
+        InputStream inputStream = null;
+        byte[] bytes = new byte[0];
+        try {
+            inputStream = new FileInputStream(file);
+            bytes = IOUtils.toByteArray(inputStream);
+        } catch (FileNotFoundException e) {
+            e.printStackTrace();
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        } finally {
+            IOUtils.closeQuietly(inputStream);
+        }
+
+        return new ByteArrayInputStream(bytes);
+
+    }
+}

+ 56 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/preview/product/MinioPreviewer.java

@@ -0,0 +1,56 @@
+package com.qiwenshare.ufop.operation.preview.product;
+
+import com.qiwenshare.ufop.config.MinioConfig;
+import com.qiwenshare.ufop.domain.ThumbImage;
+import com.qiwenshare.ufop.operation.preview.Previewer;
+import com.qiwenshare.ufop.operation.preview.domain.PreviewFile;
+import io.minio.GetObjectArgs;
+import io.minio.MinioClient;
+import io.minio.errors.MinioException;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.*;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+
+@Getter
+@Setter
+@Slf4j
+public class MinioPreviewer extends Previewer {
+    private MinioConfig minioConfig;
+
+    public MinioPreviewer(){
+
+    }
+
+    public MinioPreviewer(MinioConfig minioConfig, ThumbImage thumbImage) {
+        setMinioConfig(minioConfig);
+        setThumbImage(thumbImage);
+    }
+
+    @Override
+    protected InputStream getInputStream(PreviewFile previewFile) {
+        InputStream inputStream = null;
+        try {
+
+            MinioClient minioClient =
+                    MinioClient.builder().endpoint(minioConfig.getEndpoint())
+                            .credentials(minioConfig.getAccessKey(), minioConfig.getSecretKey()).build();
+
+            inputStream = minioClient.getObject(GetObjectArgs.builder().bucket(minioConfig.getBucketName()).object(previewFile.getFileUrl()).build());
+
+
+        } catch (MinioException e) {
+            System.out.println("Error occurred: " + e);
+        } catch (IOException | NoSuchAlgorithmException | InvalidKeyException e) {
+            log.error(e.getMessage());
+        }
+
+
+        return inputStream;
+    }
+
+
+}

+ 44 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/preview/product/QiniuyunKodoPreviewer.java

@@ -0,0 +1,44 @@
+package com.qiwenshare.ufop.operation.preview.product;
+
+import com.qiniu.util.Auth;
+import com.qiwenshare.common.util.HttpsUtils;
+import com.qiwenshare.ufop.config.QiniuyunConfig;
+import com.qiwenshare.ufop.domain.ThumbImage;
+import com.qiwenshare.ufop.operation.preview.Previewer;
+import com.qiwenshare.ufop.operation.preview.domain.PreviewFile;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.InputStream;
+
+@Getter
+@Setter
+@Slf4j
+public class QiniuyunKodoPreviewer extends Previewer {
+
+
+    private QiniuyunConfig qiniuyunConfig;
+
+    public QiniuyunKodoPreviewer(){
+
+    }
+
+    public QiniuyunKodoPreviewer(QiniuyunConfig qiniuyunConfig, ThumbImage thumbImage) {
+        this.qiniuyunConfig = qiniuyunConfig;
+        setThumbImage(thumbImage);
+    }
+
+
+    @Override
+    protected InputStream getInputStream(PreviewFile previewFile) {
+
+        Auth auth = Auth.create(qiniuyunConfig.getKodo().getAccessKey(), qiniuyunConfig.getKodo().getSecretKey());
+
+        String urlString = auth.privateDownloadUrl(qiniuyunConfig.getKodo().getDomain() + "/" + previewFile.getFileUrl());
+
+        return HttpsUtils.doGet(urlString, null);
+    }
+
+
+}

+ 7 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/read/Reader.java

@@ -0,0 +1,7 @@
+package com.qiwenshare.ufop.operation.read;
+
+import com.qiwenshare.ufop.operation.read.domain.ReadFile;
+
+public abstract class Reader {
+    public abstract String read(ReadFile readFile);
+}

+ 8 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/read/domain/ReadFile.java

@@ -0,0 +1,8 @@
+package com.qiwenshare.ufop.operation.read.domain;
+
+import lombok.Data;
+
+@Data
+public class ReadFile {
+    private String fileUrl;
+}

+ 53 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/read/product/AliyunOSSReader.java

@@ -0,0 +1,53 @@
+package com.qiwenshare.ufop.operation.read.product;
+
+import com.aliyun.oss.OSS;
+import com.aliyun.oss.model.OSSObject;
+import com.qiwenshare.ufop.config.AliyunConfig;
+import com.qiwenshare.ufop.exception.operation.ReadException;
+import com.qiwenshare.ufop.operation.read.Reader;
+import com.qiwenshare.ufop.operation.read.domain.ReadFile;
+import com.qiwenshare.ufop.util.AliyunUtils;
+import com.qiwenshare.ufop.util.UFOPUtils;
+import com.qiwenshare.ufop.util.ReadFileUtils;
+import org.apache.commons.io.FilenameUtils;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public class AliyunOSSReader extends Reader {
+
+    private AliyunConfig aliyunConfig;
+
+    public AliyunOSSReader(){
+
+    }
+
+    public AliyunOSSReader(AliyunConfig aliyunConfig) {
+        this.aliyunConfig = aliyunConfig;
+    }
+
+    @Override
+    public String read(ReadFile readFile) {
+        String fileUrl = readFile.getFileUrl();
+        String fileType = FilenameUtils.getExtension(fileUrl);
+        OSS ossClient = AliyunUtils.getOSSClient(aliyunConfig);
+        OSSObject ossObject = ossClient.getObject(aliyunConfig.getOss().getBucketName(),
+                UFOPUtils.getAliyunObjectNameByFileUrl(fileUrl));
+        InputStream inputStream = ossObject.getObjectContent();
+        try {
+            return ReadFileUtils.getContentByInputStream(fileType, inputStream);
+        } catch (IOException e) {
+            throw new ReadException("读取文件失败", e);
+        } finally {
+            ossClient.shutdown();
+        }
+    }
+
+    public InputStream getInputStream(String fileUrl) {
+        OSS ossClient = AliyunUtils.getOSSClient(aliyunConfig);
+        OSSObject ossObject = ossClient.getObject(aliyunConfig.getOss().getBucketName(),
+                UFOPUtils.getAliyunObjectNameByFileUrl(fileUrl));
+        return ossObject.getObjectContent();
+    }
+
+}

+ 43 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/read/product/FastDFSReader.java

@@ -0,0 +1,43 @@
+package com.qiwenshare.ufop.operation.read.product;
+
+import com.github.tobato.fastdfs.proto.storage.DownloadByteArray;
+import com.github.tobato.fastdfs.service.FastFileStorageClient;
+import com.qiwenshare.ufop.exception.operation.ReadException;
+import com.qiwenshare.ufop.operation.read.Reader;
+import com.qiwenshare.ufop.operation.read.domain.ReadFile;
+import com.qiwenshare.ufop.util.ReadFileUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.io.FilenameUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+@Slf4j
+@Component
+public class FastDFSReader extends Reader {
+    @Autowired
+    private FastFileStorageClient fastFileStorageClient;
+    @Override
+    public String read(ReadFile readFile) {
+
+        String fileUrl = readFile.getFileUrl();
+        String fileType = FilenameUtils.getExtension(fileUrl);
+        try {
+            return ReadFileUtils.getContentByInputStream(fileType, getInputStream(readFile.getFileUrl()));
+        } catch (IOException e) {
+            throw new ReadException("读取文件失败", e);
+        }
+    }
+
+    public InputStream getInputStream(String fileUrl) {
+        String group;
+        group = "group1";
+        String path = fileUrl.substring(fileUrl.indexOf("/") + 1);
+        DownloadByteArray downloadByteArray = new DownloadByteArray();
+        byte[] bytes = fastFileStorageClient.downloadFile(group, path, downloadByteArray);
+        return new ByteArrayInputStream(bytes);
+    }
+}

+ 31 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/read/product/LocalStorageReader.java

@@ -0,0 +1,31 @@
+package com.qiwenshare.ufop.operation.read.product;
+
+import com.qiwenshare.ufop.exception.operation.ReadException;
+import com.qiwenshare.ufop.operation.read.Reader;
+import com.qiwenshare.ufop.operation.read.domain.ReadFile;
+import com.qiwenshare.ufop.util.UFOPUtils;
+import com.qiwenshare.ufop.util.ReadFileUtils;
+import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.io.IOUtils;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+
+public class LocalStorageReader extends Reader {
+    @Override
+    public String read(ReadFile readFile) {
+
+        String fileContent;
+        FileInputStream fileInputStream = null;
+        try {
+            String extendName = FilenameUtils.getExtension(readFile.getFileUrl());
+            fileInputStream = new FileInputStream(UFOPUtils.getStaticPath() + readFile.getFileUrl());
+            fileContent = ReadFileUtils.getContentByInputStream(extendName, fileInputStream);
+        } catch (IOException e) {
+            throw new ReadException("文件读取出现异常", e);
+        } finally {
+            IOUtils.closeQuietly(fileInputStream);
+        }
+        return fileContent;
+    }
+}

+ 65 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/read/product/MinioReader.java

@@ -0,0 +1,65 @@
+package com.qiwenshare.ufop.operation.read.product;
+
+import com.qiwenshare.ufop.config.MinioConfig;
+import com.qiwenshare.ufop.exception.operation.ReadException;
+import com.qiwenshare.ufop.operation.read.Reader;
+import com.qiwenshare.ufop.operation.read.domain.ReadFile;
+import com.qiwenshare.ufop.util.ReadFileUtils;
+import io.minio.GetObjectArgs;
+import io.minio.MinioClient;
+import io.minio.errors.MinioException;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.io.FilenameUtils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+
+@Slf4j
+public class MinioReader extends Reader {
+
+    private MinioConfig minioConfig;
+
+    public MinioReader(){
+
+    }
+
+    public MinioReader(MinioConfig minioConfig) {
+        this.minioConfig = minioConfig;
+    }
+
+    @Override
+    public String read(ReadFile readFile) {
+        String fileUrl = readFile.getFileUrl();
+        String fileType = FilenameUtils.getExtension(fileUrl);
+        try {
+            return ReadFileUtils.getContentByInputStream(fileType, getInputStream(readFile.getFileUrl()));
+        } catch (IOException e) {
+            throw new ReadException("读取文件失败", e);
+        }
+    }
+
+    protected InputStream getInputStream(String fileUrl) {
+        InputStream inputStream = null;
+        try {
+
+            MinioClient minioClient =
+                    MinioClient.builder().endpoint(minioConfig.getEndpoint())
+                            .credentials(minioConfig.getAccessKey(), minioConfig.getSecretKey()).build();
+
+            inputStream = minioClient.getObject(GetObjectArgs.builder().bucket(minioConfig.getBucketName()).object(fileUrl).build());
+
+
+        } catch (MinioException e) {
+            System.out.println("Error occurred: " + e);
+        } catch (IOException | NoSuchAlgorithmException | InvalidKeyException e) {
+            log.error(e.getMessage());
+        }
+
+
+        return inputStream;
+    }
+
+
+}

+ 49 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/read/product/QiniuyunKodoReader.java

@@ -0,0 +1,49 @@
+package com.qiwenshare.ufop.operation.read.product;
+
+import com.qiniu.util.Auth;
+import com.qiwenshare.common.util.HttpsUtils;
+import com.qiwenshare.ufop.config.QiniuyunConfig;
+import com.qiwenshare.ufop.exception.operation.ReadException;
+import com.qiwenshare.ufop.operation.read.Reader;
+import com.qiwenshare.ufop.operation.read.domain.ReadFile;
+import com.qiwenshare.ufop.util.ReadFileUtils;
+import org.apache.commons.io.FilenameUtils;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public class QiniuyunKodoReader extends Reader {
+
+    private QiniuyunConfig qiniuyunConfig;
+
+    public QiniuyunKodoReader(){
+
+    }
+
+    public QiniuyunKodoReader(QiniuyunConfig qiniuyunConfig) {
+        this.qiniuyunConfig = qiniuyunConfig;
+    }
+
+    @Override
+    public String read(ReadFile readFile) {
+        String fileUrl = readFile.getFileUrl();
+        String fileType = FilenameUtils.getExtension(fileUrl);
+        try {
+            return ReadFileUtils.getContentByInputStream(fileType, getInputStream(readFile.getFileUrl()));
+        } catch (IOException e) {
+            throw new ReadException("读取文件失败", e);
+        }
+    }
+
+    public InputStream getInputStream(String fileUrl) {
+        Auth auth = Auth.create(qiniuyunConfig.getKodo().getAccessKey(), qiniuyunConfig.getKodo().getSecretKey());
+
+        String urlString = auth.privateDownloadUrl(qiniuyunConfig.getKodo().getDomain() + "/" + fileUrl);
+
+
+
+        return HttpsUtils.doGet(urlString, null);
+    }
+
+
+}

+ 208 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/upload/Uploader.java

@@ -0,0 +1,208 @@
+package com.qiwenshare.ufop.operation.upload;
+
+import com.qiwenshare.ufop.exception.operation.UploadException;
+import com.qiwenshare.ufop.operation.upload.domain.UploadFile;
+import com.qiwenshare.ufop.operation.upload.domain.UploadFileResult;
+import com.qiwenshare.ufop.operation.upload.request.QiwenMultipartFile;
+import com.qiwenshare.ufop.util.RedisUtil;
+import com.qiwenshare.ufop.util.concurrent.locks.RedisLock;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.fileupload.servlet.ServletFileUpload;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+import org.springframework.stereotype.Component;
+import org.springframework.web.multipart.MultipartFile;
+import org.springframework.web.multipart.support.StandardMultipartHttpServletRequest;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
+
+@Slf4j
+@Component
+public abstract class Uploader {
+    @Resource
+    RedisLock redisLock;
+    @Resource
+    RedisUtil redisUtil;
+
+    /**
+     * 普通上传
+     * @param httpServletRequest http的request请求
+     * @return 文件列表
+     */
+    public List<UploadFileResult> upload(HttpServletRequest httpServletRequest) {
+
+        UploadFile uploadFile = new UploadFile();
+        uploadFile.setChunkNumber(1);
+        uploadFile.setChunkSize(0);
+        uploadFile.setTotalChunks(1);
+        uploadFile.setIdentifier(UUID.randomUUID().toString());
+
+        return upload(httpServletRequest, uploadFile);
+    }
+
+    /**
+     * 分片上传
+     * @param httpServletRequest http的request请求
+     * @param uploadFile 分片上传参数
+     * @return 文件列表
+     */
+    public List<UploadFileResult> upload(HttpServletRequest httpServletRequest, UploadFile uploadFile) {
+
+        List<UploadFileResult> uploadFileResultList = new ArrayList<>();
+        StandardMultipartHttpServletRequest request = (StandardMultipartHttpServletRequest) httpServletRequest;
+
+        boolean isMultipart = ServletFileUpload.isMultipartContent(request);
+        if (!isMultipart) {
+            throw new UploadException("未包含文件上传域");
+        }
+
+        try {
+
+            Iterator<String> iter = request.getFileNames();
+            while (iter.hasNext()) {
+                List<MultipartFile> multipartFileList = request.getFiles(iter.next());
+                for (MultipartFile multipartFile : multipartFileList) {
+                    QiwenMultipartFile qiwenMultipartFile = new QiwenMultipartFile(multipartFile);
+                    UploadFileResult uploadFileResult = doUploadFlow(qiwenMultipartFile, uploadFile);
+                    uploadFileResultList.add(uploadFileResult);
+                }
+            }
+        } catch (Exception e) {
+            throw new UploadException(e);
+        }
+
+        return uploadFileResultList;
+    }
+
+    protected UploadFileResult doUploadFlow(QiwenMultipartFile qiwenMultipartFile, UploadFile uploadFile) {
+
+        UploadFileResult uploadFileResult;
+        try {
+            rectifier(qiwenMultipartFile, uploadFile);
+            uploadFileResult = organizationalResults(qiwenMultipartFile, uploadFile);
+        } catch (Exception e) {
+            throw new UploadException(e);
+        }
+
+        return uploadFileResult;
+    }
+
+    /**
+     * 取消上传
+     * @param uploadFile 分片上传参数
+     */
+    public abstract void cancelUpload(UploadFile uploadFile);
+
+    protected abstract void doUploadFileChunk(QiwenMultipartFile qiwenMultipartFile, UploadFile uploadFile)  throws IOException;
+    protected abstract UploadFileResult organizationalResults(QiwenMultipartFile qiwenMultipartFile, UploadFile uploadFile);
+
+    private void rectifier(QiwenMultipartFile qiwenMultipartFile, UploadFile uploadFile) {
+        String key = "QiwenUploader:Identifier:" + uploadFile.getIdentifier() + ":lock";
+        String current_upload_chunk_number = "QiwenUploader:Identifier:" + uploadFile.getIdentifier() + ":current_upload_chunk_number";
+
+        redisLock.lock(key);
+        try {
+
+            if (redisUtil.getObject(current_upload_chunk_number) == null) {
+                redisUtil.set(current_upload_chunk_number, "1", 1000 * 60 * 60);
+            }
+            int currentUploadChunkNumber = Integer.parseInt(redisUtil.getObject(current_upload_chunk_number));
+
+            if (uploadFile.getChunkNumber() != currentUploadChunkNumber) {
+                redisLock.unlock(key);
+                Thread.sleep(100);
+                while (redisLock.tryLock(key, 300, TimeUnit.SECONDS)) {
+
+                    currentUploadChunkNumber = Integer.parseInt(redisUtil.getObject(current_upload_chunk_number));
+
+                    if (uploadFile.getChunkNumber() <= currentUploadChunkNumber) {
+                        break;
+                    } else {
+
+                        if (Math.abs(currentUploadChunkNumber - uploadFile.getChunkNumber()) > 2) {
+                            log.error("传入的切片数据异常,当前应上传切片为第{}块,传入的为第{}块。", currentUploadChunkNumber, uploadFile.getChunkNumber());
+                            throw new UploadException("传入的切片数据异常");
+                        }
+                        redisLock.unlock(key);
+                    }
+                }
+            }
+
+            log.info("文件名{},正在上传第{}块, 共{}块>>>>>>>>>>", qiwenMultipartFile.getMultipartFile().getOriginalFilename(),uploadFile.getChunkNumber(), uploadFile.getTotalChunks());
+            if (uploadFile.getChunkNumber() == currentUploadChunkNumber) {
+                doUploadFileChunk(qiwenMultipartFile, uploadFile);
+                log.info("文件名{},第{}块上传成功", qiwenMultipartFile.getMultipartFile().getOriginalFilename(), uploadFile.getChunkNumber());
+                this.redisUtil.getIncr("QiwenUploader:Identifier:" + uploadFile.getIdentifier() + ":current_upload_chunk_number");
+            }
+        } catch (Exception e) {
+            log.error("第{}块上传失败,自动重试", uploadFile.getChunkNumber());
+            redisUtil.set("QiwenUploader:Identifier:" + uploadFile.getIdentifier() + ":current_upload_chunk_number", String.valueOf(uploadFile.getChunkNumber()), 1000 * 60 * 60);
+            throw new UploadException("更新远程文件出错", e);
+        } finally {
+
+            redisLock.unlock(key);
+        }
+
+    }
+
+    public synchronized boolean checkUploadStatus(UploadFile param, File confFile) throws IOException {
+        RandomAccessFile confAccessFile = new RandomAccessFile(confFile, "rw");
+        try {
+            //设置文件长度
+            confAccessFile.setLength(param.getTotalChunks());
+            //设置起始偏移量
+            confAccessFile.seek(param.getChunkNumber() - 1);
+            //将指定的一个字节写入文件中 127,
+            confAccessFile.write(Byte.MAX_VALUE);
+
+        } finally {
+            IOUtils.closeQuietly(confAccessFile);
+        }
+        byte[] completeStatusList = FileUtils.readFileToByteArray(confFile);
+        //创建conf文件文件长度为总分片数,每上传一个分块即向conf文件中写入一个127,那么没上传的位置就是默认的0,已上传的就是127
+        for (byte b : completeStatusList) {
+            if (b != Byte.MAX_VALUE) {
+                return false;
+            }
+        }
+        confFile.delete();
+        return true;
+    }
+
+    public void writeByteDataToFile(byte[] fileData, File file, UploadFile uploadFile) {
+        //第一步 打开将要写入的文件
+        RandomAccessFile raf;
+        try {
+            raf = new RandomAccessFile(file, "rw");
+            //第二步 打开通道
+            FileChannel fileChannel = raf.getChannel();
+            //第三步 计算偏移量
+            long position = (uploadFile.getChunkNumber() - 1) * uploadFile.getChunkSize();
+            //第四步 获取分片数据
+//            byte[] fileData = qiwenMultipartFile.getUploadBytes();
+            //第五步 写入数据
+            fileChannel.position(position);
+            fileChannel.write(ByteBuffer.wrap(fileData));
+            fileChannel.force(true);
+            fileChannel.close();
+            raf.close();
+        } catch (IOException e) {
+            throw new UploadException(e);
+        }
+
+    }
+
+
+
+}

+ 17 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/upload/domain/UploadFile.java

@@ -0,0 +1,17 @@
+package com.qiwenshare.ufop.operation.upload.domain;
+
+import lombok.Data;
+
+@Data
+public class UploadFile {
+
+    //切片上传相关参数
+    private int chunkNumber;
+    private long chunkSize;
+    private int totalChunks;
+    private String identifier;
+    private long totalSize;
+    private long currentChunkSize;
+
+
+}

+ 10 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/upload/domain/UploadFileInfo.java

@@ -0,0 +1,10 @@
+package com.qiwenshare.ufop.operation.upload.domain;
+
+import lombok.Data;
+
+@Data
+    public class UploadFileInfo {
+        private String bucketName;
+        private String key;
+        private String uploadId;
+    }

+ 20 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/upload/domain/UploadFileResult.java

@@ -0,0 +1,20 @@
+package com.qiwenshare.ufop.operation.upload.domain;
+
+import com.qiwenshare.ufop.constant.StorageTypeEnum;
+import com.qiwenshare.ufop.constant.UploadFileStatusEnum;
+import lombok.Data;
+
+import java.awt.image.BufferedImage;
+
+@Data
+public class UploadFileResult {
+    private String fileName;
+    private String extendName;
+    private long fileSize;
+    private String fileUrl;
+    private String identifier;
+    private StorageTypeEnum storageType;
+    private UploadFileStatusEnum status;
+    private BufferedImage bufferedImage;
+
+}

+ 185 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/upload/product/AliyunOSSUploader.java

@@ -0,0 +1,185 @@
+package com.qiwenshare.ufop.operation.upload.product;
+
+
+import com.alibaba.fastjson2.JSON;
+import com.aliyun.oss.OSS;
+import com.aliyun.oss.model.*;
+import com.qiwenshare.ufop.config.AliyunConfig;
+import com.qiwenshare.ufop.constant.StorageTypeEnum;
+import com.qiwenshare.ufop.constant.UploadFileStatusEnum;
+import com.qiwenshare.ufop.operation.upload.Uploader;
+import com.qiwenshare.ufop.operation.upload.domain.UploadFile;
+import com.qiwenshare.ufop.operation.upload.domain.UploadFileInfo;
+import com.qiwenshare.ufop.operation.upload.domain.UploadFileResult;
+import com.qiwenshare.ufop.operation.upload.request.QiwenMultipartFile;
+import com.qiwenshare.ufop.util.AliyunUtils;
+import com.qiwenshare.ufop.util.RedisUtil;
+import com.qiwenshare.ufop.util.UFOPUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.io.IOUtils;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import javax.imageio.ImageIO;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.*;
+
+@Slf4j
+@Component
+public class AliyunOSSUploader extends Uploader {
+
+    @Resource
+    RedisUtil redisUtil;
+
+    private AliyunConfig aliyunConfig;
+
+    public AliyunOSSUploader(){
+
+    }
+
+    public AliyunOSSUploader(AliyunConfig aliyunConfig) {
+        this.aliyunConfig = aliyunConfig;
+    }
+
+    @Override
+    protected void doUploadFileChunk(QiwenMultipartFile qiwenMultipartFile, UploadFile uploadFile) throws IOException {
+
+        OSS ossClient = AliyunUtils.getOSSClient(aliyunConfig);
+        try {
+            UploadFileInfo uploadFileInfo = JSON.parseObject(redisUtil.getObject("QiwenUploader:Identifier:" + uploadFile.getIdentifier() + ":uploadPartRequest"), UploadFileInfo.class);
+            String fileUrl = qiwenMultipartFile.getFileUrl();
+            if (uploadFileInfo == null) {
+
+                InitiateMultipartUploadRequest request = new InitiateMultipartUploadRequest(aliyunConfig.getOss().getBucketName(), fileUrl);
+                InitiateMultipartUploadResult upresult = ossClient.initiateMultipartUpload(request);
+                String uploadId = upresult.getUploadId();
+
+                uploadFileInfo = new UploadFileInfo();
+                uploadFileInfo.setBucketName(aliyunConfig.getOss().getBucketName());
+                uploadFileInfo.setKey(fileUrl);
+                uploadFileInfo.setUploadId(uploadId);
+
+                redisUtil.set("QiwenUploader:Identifier:" + uploadFile.getIdentifier() + ":uploadPartRequest", JSON.toJSONString(uploadFileInfo));
+
+            }
+
+            UploadPartRequest uploadPartRequest = new UploadPartRequest();
+            uploadPartRequest.setBucketName(uploadFileInfo.getBucketName());
+            uploadPartRequest.setKey(uploadFileInfo.getKey());
+            uploadPartRequest.setUploadId(uploadFileInfo.getUploadId());
+            uploadPartRequest.setInputStream(qiwenMultipartFile.getUploadInputStream());
+            uploadPartRequest.setPartSize(qiwenMultipartFile.getSize());
+            uploadPartRequest.setPartNumber(uploadFile.getChunkNumber());
+            log.debug(JSON.toJSONString(uploadPartRequest));
+
+            UploadPartResult uploadPartResult = ossClient.uploadPart(uploadPartRequest);
+
+            log.debug("上传结果:" + JSON.toJSONString(uploadPartResult));
+
+            if (redisUtil.hasKey("QiwenUploader:Identifier:" + uploadFile.getIdentifier() + ":partETags")) {
+                List<PartETag> partETags = JSON.parseArray(redisUtil.getObject("QiwenUploader:Identifier:" + uploadFile.getIdentifier() + ":partETags"), PartETag.class);
+                partETags.add(uploadPartResult.getPartETag());
+                redisUtil.set("QiwenUploader:Identifier:" + uploadFile.getIdentifier() + ":partETags", JSON.toJSONString(partETags));
+            } else {
+                List<PartETag> partETags = new ArrayList<>();
+                partETags.add(uploadPartResult.getPartETag());
+                redisUtil.set("QiwenUploader:Identifier:" + uploadFile.getIdentifier() + ":partETags", JSON.toJSONString(partETags));
+            }
+        } finally {
+            ossClient.shutdown();
+        }
+
+
+    }
+
+    @Override
+    protected UploadFileResult organizationalResults(QiwenMultipartFile qiwenMultipartFile, UploadFile uploadFile) {
+        UploadFileResult uploadFileResult = new UploadFileResult();
+        UploadFileInfo uploadFileInfo = JSON.parseObject(redisUtil.getObject("QiwenUploader:Identifier:" + uploadFile.getIdentifier() + ":uploadPartRequest"), UploadFileInfo.class);
+
+        uploadFileResult.setFileUrl(uploadFileInfo.getKey());
+        uploadFileResult.setFileName(qiwenMultipartFile.getFileName());
+        uploadFileResult.setExtendName(qiwenMultipartFile.getExtendName());
+        uploadFileResult.setFileSize(uploadFile.getTotalSize());
+        if (uploadFile.getTotalChunks() == 1) {
+            uploadFileResult.setFileSize(qiwenMultipartFile.getSize());
+        }
+        uploadFileResult.setStorageType(StorageTypeEnum.ALIYUN_OSS);
+        uploadFileResult.setIdentifier(uploadFile.getIdentifier());
+        if (uploadFile.getChunkNumber() == uploadFile.getTotalChunks()) {
+            log.info("分片上传完成");
+            completeMultipartUpload(uploadFile);
+            redisUtil.deleteKey("QiwenUploader:Identifier:" + uploadFile.getIdentifier() + ":current_upload_chunk_number");
+            redisUtil.deleteKey("QiwenUploader:Identifier:" + uploadFile.getIdentifier() + ":partETags");
+            redisUtil.deleteKey("QiwenUploader:Identifier:" + uploadFile.getIdentifier() + ":uploadPartRequest");
+            if (UFOPUtils.isImageFile(uploadFileResult.getExtendName())) {
+
+                OSS ossClient = AliyunUtils.getOSSClient(aliyunConfig);
+                OSSObject ossObject = ossClient.getObject(aliyunConfig.getOss().getBucketName(),
+                        UFOPUtils.getAliyunObjectNameByFileUrl(uploadFileResult.getFileUrl()));
+                InputStream is = ossObject.getObjectContent();
+                BufferedImage src;
+                try {
+                    src = ImageIO.read(is);
+                    uploadFileResult.setBufferedImage(src);
+                } catch (IOException e) {
+                    e.printStackTrace();
+                } finally {
+                    IOUtils.closeQuietly(is);
+                }
+
+            }
+            uploadFileResult.setStatus(UploadFileStatusEnum.SUCCESS);
+        } else {
+            uploadFileResult.setStatus(UploadFileStatusEnum.UNCOMPLATE);
+
+        }
+        return uploadFileResult;
+    }
+
+
+    /**
+     * 将文件分块进行升序排序并执行文件上传。
+     * @param uploadFile 上传信息
+     */
+    private void completeMultipartUpload(UploadFile uploadFile) {
+
+        List<PartETag> partETags = JSON.parseArray(redisUtil.getObject("QiwenUploader:Identifier:" + uploadFile.getIdentifier() + ":partETags"), PartETag.class);
+
+        partETags.sort(Comparator.comparingInt(PartETag::getPartNumber));
+
+        UploadFileInfo uploadFileInfo = JSON.parseObject(redisUtil.getObject("QiwenUploader:Identifier:" + uploadFile.getIdentifier() + ":uploadPartRequest"), UploadFileInfo.class);
+
+        CompleteMultipartUploadRequest completeMultipartUploadRequest =
+                new CompleteMultipartUploadRequest(aliyunConfig.getOss().getBucketName(),
+                        uploadFileInfo.getKey(),
+                        uploadFileInfo.getUploadId(),
+                        partETags);
+        OSS ossClient = AliyunUtils.getOSSClient(aliyunConfig);
+        // 完成上传。
+        ossClient.completeMultipartUpload(completeMultipartUploadRequest);
+        ossClient.shutdown();
+
+    }
+
+    /**
+     * 取消上传
+     */
+    @Override
+    public void cancelUpload(UploadFile uploadFile) {
+
+        UploadFileInfo uploadFileInfo = JSON.parseObject(redisUtil.getObject("QiwenUploader:Identifier:" + uploadFile.getIdentifier() + ":uploadPartRequest"), UploadFileInfo.class);
+
+        OSS ossClient = AliyunUtils.getOSSClient(aliyunConfig);
+        AbortMultipartUploadRequest abortMultipartUploadRequest =
+                new AbortMultipartUploadRequest(aliyunConfig.getOss().getBucketName(),
+                        uploadFileInfo.getKey(),
+                        uploadFileInfo.getUploadId());
+        ossClient.abortMultipartUpload(abortMultipartUploadRequest);
+        ossClient.shutdown();
+    }
+
+
+}

+ 140 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/upload/product/FastDFSUploader.java

@@ -0,0 +1,140 @@
+package com.qiwenshare.ufop.operation.upload.product;
+
+import com.github.tobato.fastdfs.domain.StorePath;
+import com.github.tobato.fastdfs.exception.FdfsServerException;
+import com.github.tobato.fastdfs.proto.storage.DownloadByteArray;
+import com.github.tobato.fastdfs.service.AppendFileStorageClient;
+import com.github.tobato.fastdfs.service.FastFileStorageClient;
+import com.qiwenshare.ufop.constant.StorageTypeEnum;
+import com.qiwenshare.ufop.constant.UploadFileStatusEnum;
+import com.qiwenshare.ufop.exception.operation.UploadException;
+import com.qiwenshare.ufop.operation.upload.Uploader;
+import com.qiwenshare.ufop.operation.upload.domain.UploadFile;
+import com.qiwenshare.ufop.operation.upload.domain.UploadFileResult;
+import com.qiwenshare.ufop.operation.upload.request.QiwenMultipartFile;
+import com.qiwenshare.ufop.util.RedisUtil;
+import com.qiwenshare.ufop.util.UFOPUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.io.IOUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import javax.imageio.ImageIO;
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+@Component
+@Slf4j
+public class FastDFSUploader extends Uploader {
+
+    @Resource
+    AppendFileStorageClient defaultAppendFileStorageClient;
+    @Autowired
+    private FastFileStorageClient fastFileStorageClient;
+
+    @Resource
+    RedisUtil redisUtil;
+
+    @Override
+    public void doUploadFileChunk(QiwenMultipartFile qiwenMultipartFile, UploadFile uploadFile) throws IOException {
+        StorePath storePath;
+
+        if (uploadFile.getChunkNumber() <= 1) {
+            log.info("上传第一块");
+
+            storePath = defaultAppendFileStorageClient.uploadAppenderFile("group1", qiwenMultipartFile.getUploadInputStream(),
+                    qiwenMultipartFile.getSize(), qiwenMultipartFile.getExtendName());
+            // 记录第一个分片上传的大小
+            redisUtil.set("QiwenUploader:Identifier:" + uploadFile.getIdentifier() + ":uploaded_size", String.valueOf(qiwenMultipartFile.getSize()), 1000 * 60 * 60);
+
+            log.info("第一块上传完成");
+            if (storePath == null) {
+                redisUtil.set("QiwenUploader:Identifier:" + uploadFile.getIdentifier() + ":current_upload_chunk_number", String.valueOf(uploadFile.getChunkNumber()), 1000 * 60 * 60);
+
+                log.info("获取远程文件路径出错");
+                throw new UploadException("获取远程文件路径出错");
+            }
+
+            redisUtil.set("QiwenUploader:Identifier:" + uploadFile.getIdentifier() + ":storage_path", storePath.getPath(), 1000 * 60 * 60);
+
+            log.info("上传文件 result = {}", storePath.getPath());
+        } else {
+            log.info("正在上传第{}块:" , uploadFile.getChunkNumber());
+
+            String path = redisUtil.getObject("QiwenUploader:Identifier:" + uploadFile.getIdentifier() + ":storage_path");
+
+            if (path == null) {
+                log.error("无法获取已上传服务器文件地址");
+                throw new UploadException("无法获取已上传服务器文件地址");
+            }
+
+            String uploadedSizeStr = redisUtil.getObject("QiwenUploader:Identifier:" + uploadFile.getIdentifier() + ":uploaded_size");
+            long alreadySize = Long.parseLong(uploadedSizeStr);
+
+            // 追加方式实际实用如果中途出错多次,可能会出现重复追加情况,这里改成修改模式,即时多次传来重复文件块,依然可以保证文件拼接正确
+            defaultAppendFileStorageClient.modifyFile("group1", path, qiwenMultipartFile.getUploadInputStream(),
+                    qiwenMultipartFile.getSize(), alreadySize);
+            // 记录分片上传的大小
+            redisUtil.set("QiwenUploader:Identifier:" + uploadFile.getIdentifier() + ":uploaded_size", String.valueOf(alreadySize + qiwenMultipartFile.getSize()), 1000 * 60 * 60);
+
+        }
+    }
+
+    @Override
+    protected UploadFileResult organizationalResults(QiwenMultipartFile qiwenMultipartFile, UploadFile uploadFile) {
+        UploadFileResult uploadFileResult = new UploadFileResult();
+
+        String path = redisUtil.getObject("QiwenUploader:Identifier:" + uploadFile.getIdentifier() + ":storage_path");
+        uploadFileResult.setFileUrl(path);
+        uploadFileResult.setFileName(qiwenMultipartFile.getFileName());
+        uploadFileResult.setExtendName(qiwenMultipartFile.getExtendName());
+        uploadFileResult.setFileSize(uploadFile.getTotalSize());
+        if (uploadFile.getTotalChunks() == 1) {
+            uploadFileResult.setFileSize(qiwenMultipartFile.getSize());
+        }
+        uploadFileResult.setStorageType(StorageTypeEnum.FAST_DFS);
+        uploadFileResult.setIdentifier(uploadFile.getIdentifier());
+
+        if (uploadFile.getChunkNumber() == uploadFile.getTotalChunks()) {
+            log.info("分片上传完成");
+            redisUtil.deleteKey("QiwenUploader:Identifier:" + uploadFile.getIdentifier() + ":current_upload_chunk_number");
+            redisUtil.deleteKey("QiwenUploader:Identifier:" + uploadFile.getIdentifier() + ":storage_path");
+            redisUtil.deleteKey("QiwenUploader:Identifier:" + uploadFile.getIdentifier() + ":uploaded_size");
+            if (UFOPUtils.isImageFile(uploadFileResult.getExtendName())) {
+                String group = "group1";
+                String path1 = uploadFileResult.getFileUrl().substring(uploadFileResult.getFileUrl().indexOf("/") + 1);
+                DownloadByteArray downloadByteArray = new DownloadByteArray();
+                byte[] bytes = defaultAppendFileStorageClient.downloadFile(group, path1, downloadByteArray);
+                InputStream is = new ByteArrayInputStream(bytes);
+
+                BufferedImage src;
+                try {
+                    src = ImageIO.read(is);
+                    uploadFileResult.setBufferedImage(src);
+                } catch (IOException e) {
+                    e.printStackTrace();
+                } finally {
+                    IOUtils.closeQuietly(is);
+                }
+
+            }
+            uploadFileResult.setStatus(UploadFileStatusEnum.SUCCESS);
+        } else {
+            uploadFileResult.setStatus(UploadFileStatusEnum.UNCOMPLATE);
+        }
+        return uploadFileResult;
+    }
+
+    @Override
+    public void cancelUpload(UploadFile uploadFile) {
+        String path = redisUtil.getObject("QiwenUploader:Identifier:" + uploadFile.getIdentifier() + ":storage_path");
+        try {
+            fastFileStorageClient.deleteFile(path.replace("M00", "group1"));
+        } catch (FdfsServerException e) {
+            log.error(e.getMessage());
+        }
+    }
+}

+ 130 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/upload/product/LocalStorageUploader.java

@@ -0,0 +1,130 @@
+package com.qiwenshare.ufop.operation.upload.product;
+
+import com.qiwenshare.ufop.constant.StorageTypeEnum;
+import com.qiwenshare.ufop.constant.UploadFileStatusEnum;
+import com.qiwenshare.ufop.exception.operation.UploadException;
+import com.qiwenshare.ufop.operation.upload.Uploader;
+import com.qiwenshare.ufop.operation.upload.domain.UploadFile;
+import com.qiwenshare.ufop.operation.upload.domain.UploadFileResult;
+import com.qiwenshare.ufop.operation.upload.request.QiwenMultipartFile;
+import com.qiwenshare.ufop.util.UFOPUtils;
+import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Component;
+
+import javax.imageio.ImageIO;
+import java.awt.image.BufferedImage;
+import java.io.*;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+import java.util.*;
+
+@Component
+public class LocalStorageUploader extends Uploader {
+
+    public static Map<String, String> FILE_URL_MAP = new HashMap<>();
+
+    protected UploadFileResult doUploadFlow(QiwenMultipartFile qiwenMultipartFile, UploadFile uploadFile) {
+        UploadFileResult uploadFileResult = new UploadFileResult();
+        try {
+            String fileUrl = UFOPUtils.getUploadFileUrl(uploadFile.getIdentifier(), qiwenMultipartFile.getExtendName());
+            if (StringUtils.isNotEmpty(FILE_URL_MAP.get(uploadFile.getIdentifier()))) {
+                fileUrl = FILE_URL_MAP.get(uploadFile.getIdentifier());
+            } else {
+                FILE_URL_MAP.put(uploadFile.getIdentifier(), fileUrl);
+            }
+            String tempFileUrl = fileUrl + "_tmp";
+            String confFileUrl = fileUrl.replace("." + qiwenMultipartFile.getExtendName(), ".conf");
+
+            File file = new File(UFOPUtils.getStaticPath() + fileUrl);
+            File tempFile = new File(UFOPUtils.getStaticPath() + tempFileUrl);
+            File confFile = new File(UFOPUtils.getStaticPath() + confFileUrl);
+
+            //第一步 打开将要写入的文件
+            RandomAccessFile raf = new RandomAccessFile(tempFile, "rw");
+            //第二步 打开通道
+            try {
+                FileChannel fileChannel = raf.getChannel();
+                //第三步 计算偏移量
+                long position = (uploadFile.getChunkNumber() - 1) * uploadFile.getChunkSize();
+                //第四步 获取分片数据
+                byte[] fileData = qiwenMultipartFile.getUploadBytes();
+                //第五步 写入数据
+                fileChannel.position(position);
+                fileChannel.write(ByteBuffer.wrap(fileData));
+                fileChannel.force(true);
+                fileChannel.close();
+            } finally {
+                IOUtils.closeQuietly(raf);
+            }
+
+            //判断是否完成文件的传输并进行校验与重命名
+            boolean isComplete = checkUploadStatus(uploadFile, confFile);
+            uploadFileResult.setFileUrl(fileUrl);
+            uploadFileResult.setFileName(qiwenMultipartFile.getFileName());
+            uploadFileResult.setExtendName(qiwenMultipartFile.getExtendName());
+            uploadFileResult.setFileSize(uploadFile.getTotalSize());
+            uploadFileResult.setStorageType(StorageTypeEnum.LOCAL);
+
+            if (uploadFile.getTotalChunks() == 1) {
+                uploadFileResult.setFileSize(qiwenMultipartFile.getSize());
+            }
+            uploadFileResult.setIdentifier(uploadFile.getIdentifier());
+            if (isComplete) {
+                tempFile.renameTo(file);
+                FILE_URL_MAP.remove(uploadFile.getIdentifier());
+
+                if (UFOPUtils.isImageFile(uploadFileResult.getExtendName())) {
+
+                    InputStream is = null;
+                    try {
+                        is = new FileInputStream(UFOPUtils.getLocalSaveFile(fileUrl));
+
+                        BufferedImage src = ImageIO.read(is);
+                        uploadFileResult.setBufferedImage(src);
+                    } catch (IOException e) {
+                        e.printStackTrace();
+                    } finally {
+                        IOUtils.closeQuietly(is);
+                    }
+                }
+
+                uploadFileResult.setStatus(UploadFileStatusEnum.SUCCESS);
+            } else {
+                uploadFileResult.setStatus(UploadFileStatusEnum.UNCOMPLATE);
+            }
+        } catch (IOException e) {
+            throw new UploadException(e);
+        }
+
+
+        return uploadFileResult;
+    }
+
+    @Override
+    public void cancelUpload(UploadFile uploadFile) {
+        String fileUrl = FILE_URL_MAP.get(uploadFile.getIdentifier());
+        String tempFileUrl = fileUrl + "_tmp";
+        String confFileUrl = fileUrl.replace("." + FilenameUtils.getExtension(fileUrl), ".conf");
+        File tempFile = new File(tempFileUrl);
+        if (tempFile.exists()) {
+            tempFile.delete();
+        }
+        File confFile = new File(confFileUrl);
+        if (confFile.exists()) {
+            confFile.delete();
+        }
+    }
+
+    @Override
+    protected void doUploadFileChunk(QiwenMultipartFile qiwenMultipartFile, UploadFile uploadFile) {
+
+    }
+
+    @Override
+    protected UploadFileResult organizationalResults(QiwenMultipartFile qiwenMultipartFile, UploadFile uploadFile) {
+        return null;
+    }
+
+}

+ 145 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/upload/product/MinioUploader.java

@@ -0,0 +1,145 @@
+package com.qiwenshare.ufop.operation.upload.product;
+
+import com.qiwenshare.ufop.config.MinioConfig;
+import com.qiwenshare.ufop.constant.StorageTypeEnum;
+import com.qiwenshare.ufop.constant.UploadFileStatusEnum;
+import com.qiwenshare.ufop.exception.operation.UploadException;
+import com.qiwenshare.ufop.operation.upload.Uploader;
+import com.qiwenshare.ufop.operation.upload.domain.UploadFile;
+import com.qiwenshare.ufop.operation.upload.domain.UploadFileResult;
+import com.qiwenshare.ufop.operation.upload.request.QiwenMultipartFile;
+import com.qiwenshare.ufop.util.RedisUtil;
+import com.qiwenshare.ufop.util.UFOPUtils;
+import io.minio.*;
+import io.minio.errors.*;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.io.IOUtils;
+
+import javax.annotation.Resource;
+import javax.imageio.ImageIO;
+import java.awt.image.BufferedImage;
+import java.io.*;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+
+@Slf4j
+public class MinioUploader extends Uploader {
+
+    private MinioConfig minioConfig;
+
+    @Resource
+    RedisUtil redisUtil;
+
+    public MinioUploader(){
+
+    }
+
+    public MinioUploader(MinioConfig minioConfig){
+        this.minioConfig = minioConfig;
+    }
+
+    @Override
+    public void cancelUpload(UploadFile uploadFile) {
+    }
+
+    @Override
+    protected void doUploadFileChunk(QiwenMultipartFile qiwenMultipartFile, UploadFile uploadFile) {
+
+    }
+
+    @Override
+    protected UploadFileResult organizationalResults(QiwenMultipartFile qiwenMultipartFile, UploadFile uploadFile) {
+        return null;
+    }
+
+    protected UploadFileResult doUploadFlow(QiwenMultipartFile qiwenMultipartFile, UploadFile uploadFile) {
+        UploadFileResult uploadFileResult = new UploadFileResult();
+        try {
+            qiwenMultipartFile.getFileUrl(uploadFile.getIdentifier());
+            String fileUrl = UFOPUtils.getUploadFileUrl(uploadFile.getIdentifier(), qiwenMultipartFile.getExtendName());
+
+            File tempFile =  UFOPUtils.getTempFile(fileUrl);
+            File processFile = UFOPUtils.getProcessFile(fileUrl);
+
+            byte[] fileData = qiwenMultipartFile.getUploadBytes();
+
+            writeByteDataToFile(fileData, tempFile, uploadFile);
+
+            //判断是否完成文件的传输并进行校验与重命名
+            boolean isComplete = checkUploadStatus(uploadFile, processFile);
+            uploadFileResult.setFileUrl(fileUrl);
+            uploadFileResult.setFileName(qiwenMultipartFile.getFileName());
+            uploadFileResult.setExtendName(qiwenMultipartFile.getExtendName());
+            uploadFileResult.setFileSize(uploadFile.getTotalSize());
+            uploadFileResult.setStorageType(StorageTypeEnum.MINIO);
+
+            if (uploadFile.getTotalChunks() == 1) {
+                uploadFileResult.setFileSize(qiwenMultipartFile.getSize());
+            }
+            uploadFileResult.setIdentifier(uploadFile.getIdentifier());
+            if (isComplete) {
+
+                minioUpload(fileUrl, tempFile, uploadFile);
+                uploadFileResult.setFileUrl(fileUrl);
+                tempFile.delete();
+
+                if (UFOPUtils.isImageFile(uploadFileResult.getExtendName())) {
+                    InputStream inputStream = null;
+                    try {
+                        MinioClient minioClient =
+                                MinioClient.builder().endpoint(minioConfig.getEndpoint())
+                                        .credentials(minioConfig.getAccessKey(), minioConfig.getSecretKey()).build();
+
+                        inputStream = minioClient.getObject(GetObjectArgs.builder().bucket(minioConfig.getBucketName()).object(uploadFileResult.getFileUrl()).build());
+
+                        BufferedImage src  = ImageIO.read(inputStream);
+                        uploadFileResult.setBufferedImage(src);
+                    } catch (IOException | InternalException | XmlParserException | InvalidResponseException | InvalidKeyException | NoSuchAlgorithmException | ErrorResponseException | InsufficientDataException | ServerException e) {
+                        e.printStackTrace();
+                    } finally {
+                        IOUtils.closeQuietly(inputStream);
+                    }
+
+                }
+
+                uploadFileResult.setStatus(UploadFileStatusEnum.SUCCESS);
+            } else {
+                uploadFileResult.setStatus(UploadFileStatusEnum.UNCOMPLATE);
+            }
+        } catch (IOException e) {
+            throw new UploadException(e);
+        }
+
+
+        return uploadFileResult;
+    }
+
+
+    private void minioUpload(String fileUrl, File file,  UploadFile uploadFile) {
+        InputStream inputStream = null;
+        try {
+            MinioClient minioClient =
+                    MinioClient.builder().endpoint(minioConfig.getEndpoint())
+                            .credentials(minioConfig.getAccessKey(), minioConfig.getSecretKey()).build();
+            // 检查存储桶是否已经存在
+            boolean isExist = minioClient.bucketExists(BucketExistsArgs.builder().bucket(minioConfig.getBucketName()).build());
+            if(!isExist) {
+                minioClient.makeBucket(MakeBucketArgs.builder().bucket(minioConfig.getBucketName()).build());
+            }
+
+            inputStream = new FileInputStream(file);
+            minioClient.putObject(
+                    PutObjectArgs.builder().bucket(minioConfig.getBucketName()).object(fileUrl).stream(
+                                    inputStream, uploadFile.getTotalSize(), 1024 * 1024 * 5)
+//                            .contentType("video/mp4")
+                            .build());
+        } catch (MinioException | InvalidKeyException | NoSuchAlgorithmException | IOException e) {
+            e.printStackTrace();
+        } finally {
+            IOUtils.closeQuietly(inputStream);
+        }
+
+    }
+
+
+}

+ 165 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/upload/product/QiniuyunKodoUploader.java

@@ -0,0 +1,165 @@
+package com.qiwenshare.ufop.operation.upload.product;
+
+import com.google.gson.Gson;
+import com.qiniu.common.QiniuException;
+import com.qiniu.http.Response;
+import com.qiniu.storage.Configuration;
+import com.qiniu.storage.UploadManager;
+import com.qiniu.storage.model.DefaultPutRet;
+import com.qiniu.storage.persistent.FileRecorder;
+import com.qiniu.util.Auth;
+import com.qiwenshare.common.util.HttpsUtils;
+import com.qiwenshare.ufop.config.QiniuyunConfig;
+import com.qiwenshare.ufop.constant.StorageTypeEnum;
+import com.qiwenshare.ufop.constant.UploadFileStatusEnum;
+import com.qiwenshare.ufop.exception.UFOPException;
+import com.qiwenshare.ufop.exception.operation.UploadException;
+import com.qiwenshare.ufop.operation.upload.Uploader;
+import com.qiwenshare.ufop.operation.upload.domain.UploadFile;
+import com.qiwenshare.ufop.operation.upload.domain.UploadFileResult;
+import com.qiwenshare.ufop.operation.upload.request.QiwenMultipartFile;
+import com.qiwenshare.ufop.util.QiniuyunUtils;
+import com.qiwenshare.ufop.util.RedisUtil;
+import com.qiwenshare.ufop.util.UFOPUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.io.IOUtils;
+
+import javax.annotation.Resource;
+import javax.imageio.ImageIO;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+
+@Slf4j
+public class QiniuyunKodoUploader extends Uploader {
+
+    private QiniuyunConfig qiniuyunConfig;
+
+    @Resource
+    RedisUtil redisUtil;
+
+    public QiniuyunKodoUploader(){
+
+    }
+
+    public QiniuyunKodoUploader(QiniuyunConfig qiniuyunConfig){
+        this.qiniuyunConfig = qiniuyunConfig;
+    }
+
+    @Override
+    public void cancelUpload(UploadFile uploadFile) {
+    }
+
+    @Override
+    protected void doUploadFileChunk(QiwenMultipartFile qiwenMultipartFile, UploadFile uploadFile) {
+
+    }
+
+    @Override
+    protected UploadFileResult organizationalResults(QiwenMultipartFile qiwenMultipartFile, UploadFile uploadFile) {
+        return null;
+    }
+
+    protected UploadFileResult doUploadFlow(QiwenMultipartFile qiwenMultipartFile, UploadFile uploadFile) {
+        UploadFileResult uploadFileResult = new UploadFileResult();
+        try {
+            qiwenMultipartFile.getFileUrl(uploadFile.getIdentifier());
+            String fileUrl = UFOPUtils.getUploadFileUrl(uploadFile.getIdentifier(), qiwenMultipartFile.getExtendName());
+
+            File tempFile =  UFOPUtils.getTempFile(fileUrl);
+            File processFile = UFOPUtils.getProcessFile(fileUrl);
+
+            byte[] fileData = qiwenMultipartFile.getUploadBytes();
+
+            writeByteDataToFile(fileData, tempFile, uploadFile);
+
+            //判断是否完成文件的传输并进行校验与重命名
+            boolean isComplete = checkUploadStatus(uploadFile, processFile);
+            uploadFileResult.setFileUrl(fileUrl);
+            uploadFileResult.setFileName(qiwenMultipartFile.getFileName());
+            uploadFileResult.setExtendName(qiwenMultipartFile.getExtendName());
+            uploadFileResult.setFileSize(uploadFile.getTotalSize());
+            uploadFileResult.setStorageType(StorageTypeEnum.QINIUYUN_KODO);
+
+            if (uploadFile.getTotalChunks() == 1) {
+                uploadFileResult.setFileSize(qiwenMultipartFile.getSize());
+            }
+            uploadFileResult.setIdentifier(uploadFile.getIdentifier());
+            if (isComplete) {
+
+                qiniuUpload(fileUrl, tempFile, uploadFile);
+                uploadFileResult.setFileUrl(fileUrl);
+                boolean result = tempFile.delete();
+                if (!result) {
+                    throw new UFOPException("删除temp文件失败:目录路径:"+ tempFile.getPath());
+                }
+
+                if (UFOPUtils.isImageFile(uploadFileResult.getExtendName())) {
+                    Auth auth = Auth.create(qiniuyunConfig.getKodo().getAccessKey(), qiniuyunConfig.getKodo().getSecretKey());
+
+                    String urlString = auth.privateDownloadUrl(qiniuyunConfig.getKodo().getDomain() + "/" + uploadFileResult.getFileUrl());
+
+                    InputStream inputStream = HttpsUtils.doGet(urlString, null);
+                    BufferedImage src;
+                    try {
+                        src = ImageIO.read(inputStream);
+                        uploadFileResult.setBufferedImage(src);
+                    } catch (IOException e) {
+                        e.printStackTrace();
+                    } finally {
+                        IOUtils.closeQuietly(inputStream);
+                    }
+
+                }
+
+                uploadFileResult.setStatus(UploadFileStatusEnum.SUCCESS);
+            } else {
+                uploadFileResult.setStatus(UploadFileStatusEnum.UNCOMPLATE);
+            }
+        } catch (IOException e) {
+            throw new UploadException(e);
+        }
+
+
+        return uploadFileResult;
+    }
+
+
+    private void qiniuUpload(String fileUrl, File file,  UploadFile uploadFile) {
+        Configuration cfg = QiniuyunUtils.getCfg(qiniuyunConfig);
+        cfg.resumableUploadAPIVersion = Configuration.ResumableUploadAPIVersion.V2;// 指定分片上传版本
+        cfg.resumableUploadMaxConcurrentTaskCount = 2;  // 设置分片上传并发,1:采用同步上传;大于1:采用并发上传
+
+        Auth auth = Auth.create(qiniuyunConfig.getKodo().getAccessKey(), qiniuyunConfig.getKodo().getSecretKey());
+        String upToken = auth.uploadToken(qiniuyunConfig.getKodo().getBucketName());
+
+        String localTempDir = UFOPUtils.getStaticPath() + "temp";
+        try {
+            //设置断点续传文件进度保存目录
+            FileRecorder fileRecorder = new FileRecorder(localTempDir);
+            UploadManager uploadManager = new UploadManager(cfg, fileRecorder);
+            try {
+                Response response = uploadManager.put(file.getAbsoluteFile(), fileUrl, upToken);
+                //解析上传成功的结果
+                DefaultPutRet putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class);
+                log.info(putRet.key);
+                log.info(putRet.hash);
+            } catch (QiniuException ex) {
+                Response r = ex.response;
+                System.err.println(r.toString());
+                try {
+                    System.err.println(r.bodyString());
+                } catch (QiniuException ex2) {
+                    //ignore
+                }
+            }
+        } catch (IOException ex) {
+            ex.printStackTrace();
+        }
+
+
+    }
+
+
+}

+ 60 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/upload/request/QiwenMultipartFile.java

@@ -0,0 +1,60 @@
+package com.qiwenshare.ufop.operation.upload.request;
+
+import com.qiwenshare.ufop.util.UFOPUtils;
+import org.apache.commons.io.FilenameUtils;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.UUID;
+
+public class QiwenMultipartFile {
+
+    MultipartFile multipartFile = null;
+
+    private QiwenMultipartFile() {
+    }
+
+    public QiwenMultipartFile(MultipartFile multipartFile) {
+        this.multipartFile = multipartFile;
+    }
+
+    public String getFileName() {
+        String originalName = getMultipartFile().getOriginalFilename();
+        if (!originalName.contains(".")) {
+            return originalName;
+        }
+        return originalName.substring(0, originalName.lastIndexOf("."));
+    }
+
+    public String getExtendName() {
+        String originalName = getMultipartFile().getOriginalFilename();
+        return FilenameUtils.getExtension(originalName);
+    }
+
+    public String getFileUrl() {
+        String uuid = UUID.randomUUID().toString();
+        return UFOPUtils.getUploadFileUrl(uuid, getExtendName());
+    }
+
+    public String getFileUrl(String identify) {
+        return UFOPUtils.getUploadFileUrl(identify, getExtendName());
+    }
+
+    public InputStream getUploadInputStream() throws IOException {
+        return getMultipartFile().getInputStream();
+    }
+
+    public byte[] getUploadBytes() throws IOException {
+        return getMultipartFile().getBytes();
+    }
+
+    public long getSize() {
+        return getMultipartFile().getSize();
+    }
+
+    public MultipartFile getMultipartFile() {
+        return multipartFile;
+    }
+
+}

+ 9 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/write/Writer.java

@@ -0,0 +1,9 @@
+package com.qiwenshare.ufop.operation.write;
+
+import com.qiwenshare.ufop.operation.write.domain.WriteFile;
+
+import java.io.InputStream;
+
+public abstract class Writer {
+    public abstract void write(InputStream inputStream, WriteFile writeFile);
+}

+ 9 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/write/domain/WriteFile.java

@@ -0,0 +1,9 @@
+package com.qiwenshare.ufop.operation.write.domain;
+
+import lombok.Data;
+
+@Data
+public class WriteFile {
+    private String fileUrl;
+    private long fileSize;
+}

+ 34 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/write/product/AliyunOSSWriter.java

@@ -0,0 +1,34 @@
+package com.qiwenshare.ufop.operation.write.product;
+
+import com.aliyun.oss.OSS;
+import com.qiwenshare.ufop.config.AliyunConfig;
+import com.qiwenshare.ufop.operation.write.Writer;
+import com.qiwenshare.ufop.operation.write.domain.WriteFile;
+import com.qiwenshare.ufop.util.AliyunUtils;
+import com.qiwenshare.ufop.util.UFOPUtils;
+
+import java.io.InputStream;
+
+public class AliyunOSSWriter extends Writer {
+
+    private AliyunConfig aliyunConfig;
+
+    public AliyunOSSWriter(){
+
+    }
+
+    public AliyunOSSWriter(AliyunConfig aliyunConfig) {
+        this.aliyunConfig = aliyunConfig;
+    }
+
+    @Override
+    public void write(InputStream inputStream, WriteFile writeFile) {
+        OSS ossClient = AliyunUtils.getOSSClient(aliyunConfig);
+
+        ossClient.putObject(aliyunConfig.getOss().getBucketName(), UFOPUtils.getAliyunObjectNameByFileUrl(writeFile.getFileUrl()), inputStream);
+        ossClient.shutdown();
+    }
+
+
+
+}

+ 22 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/write/product/FastDFSWriter.java

@@ -0,0 +1,22 @@
+package com.qiwenshare.ufop.operation.write.product;
+
+import com.github.tobato.fastdfs.service.AppendFileStorageClient;
+import com.qiwenshare.ufop.operation.write.Writer;
+import com.qiwenshare.ufop.operation.write.domain.WriteFile;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import java.io.InputStream;
+
+@Component
+@Slf4j
+public class FastDFSWriter extends Writer {
+    @Resource
+    AppendFileStorageClient defaultAppendFileStorageClient;
+    @Override
+    public void write(InputStream inputStream, WriteFile writeFile) {
+        defaultAppendFileStorageClient.modifyFile("group1", writeFile.getFileUrl(), inputStream,
+                writeFile.getFileSize(), 0);
+    }
+}

+ 31 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/write/product/LocalStorageWriter.java

@@ -0,0 +1,31 @@
+package com.qiwenshare.ufop.operation.write.product;
+
+import com.qiwenshare.ufop.exception.operation.WriteException;
+import com.qiwenshare.ufop.operation.write.Writer;
+import com.qiwenshare.ufop.operation.write.domain.WriteFile;
+import com.qiwenshare.ufop.util.UFOPUtils;
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+@Slf4j
+public class LocalStorageWriter extends Writer {
+    @Override
+    public void write(InputStream inputStream, WriteFile writeFile) {
+        try (FileOutputStream out = new FileOutputStream(UFOPUtils.getStaticPath() + writeFile.getFileUrl())){
+            int read;
+            final byte[] bytes = new byte[1024];
+            while ((read = inputStream.read(bytes)) != -1) {
+                out.write(bytes, 0, read);
+            }
+            out.flush();
+        } catch (FileNotFoundException e) {
+            throw new WriteException("待写入的文件不存在:{}", e);
+        } catch (IOException e) {
+            throw new WriteException("IO异常:{}", e);
+        }
+    }
+}

+ 55 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/write/product/MinioWriter.java

@@ -0,0 +1,55 @@
+package com.qiwenshare.ufop.operation.write.product;
+
+import com.qiwenshare.ufop.config.MinioConfig;
+import com.qiwenshare.ufop.operation.write.Writer;
+import com.qiwenshare.ufop.operation.write.domain.WriteFile;
+import com.qiwenshare.ufop.util.UFOPUtils;
+import io.minio.BucketExistsArgs;
+import io.minio.MakeBucketArgs;
+import io.minio.MinioClient;
+import io.minio.PutObjectArgs;
+import io.minio.errors.MinioException;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+
+public class MinioWriter extends Writer {
+
+    private MinioConfig minioConfig;
+
+    public MinioWriter(){
+
+    }
+
+    public MinioWriter(MinioConfig minioConfig) {
+        this.minioConfig = minioConfig;
+    }
+
+    @Override
+    public void write(InputStream inputStream, WriteFile writeFile) {
+
+
+        try {
+            MinioClient minioClient =
+                    MinioClient.builder().endpoint(minioConfig.getEndpoint())
+                            .credentials(minioConfig.getAccessKey(), minioConfig.getSecretKey()).build();
+            // 检查存储桶是否已经存在
+            boolean isExist = minioClient.bucketExists(BucketExistsArgs.builder().bucket(minioConfig.getBucketName()).build());
+            if(!isExist) {
+                minioClient.makeBucket(MakeBucketArgs.builder().bucket(minioConfig.getBucketName()).build());
+            }
+
+            minioClient.putObject(
+                    PutObjectArgs.builder().bucket(minioConfig.getBucketName()).object(UFOPUtils.getAliyunObjectNameByFileUrl(writeFile.getFileUrl())).stream(
+                                    inputStream, inputStream.available(), -1)
+//                            .contentType("video/mp4")
+                            .build());
+
+        } catch (MinioException | InvalidKeyException | NoSuchAlgorithmException | IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+}

+ 65 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/operation/write/product/QiniuyunKodoWriter.java

@@ -0,0 +1,65 @@
+package com.qiwenshare.ufop.operation.write.product;
+
+import com.google.gson.Gson;
+import com.qiniu.http.Response;
+import com.qiniu.storage.Configuration;
+import com.qiniu.storage.UploadManager;
+import com.qiniu.storage.model.DefaultPutRet;
+import com.qiniu.storage.persistent.FileRecorder;
+import com.qiniu.util.Auth;
+import com.qiwenshare.ufop.config.QiniuyunConfig;
+import com.qiwenshare.ufop.exception.operation.WriteException;
+import com.qiwenshare.ufop.operation.write.Writer;
+import com.qiwenshare.ufop.operation.write.domain.WriteFile;
+import com.qiwenshare.ufop.util.QiniuyunUtils;
+import com.qiwenshare.ufop.util.UFOPUtils;
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+@Slf4j
+public class QiniuyunKodoWriter extends Writer {
+
+    private QiniuyunConfig qiniuyunConfig;
+
+    public QiniuyunKodoWriter(){
+
+    }
+
+    public QiniuyunKodoWriter(QiniuyunConfig qiniuyunConfig) {
+        this.qiniuyunConfig = qiniuyunConfig;
+    }
+
+    @Override
+    public void write(InputStream inputStream, WriteFile writeFile) {
+
+        qiniuUpload(writeFile.getFileUrl(), inputStream);
+    }
+
+    private void qiniuUpload(String fileUrl, InputStream inputStream) {
+
+        Configuration cfg = QiniuyunUtils.getCfg(qiniuyunConfig);
+        Auth auth = Auth.create(qiniuyunConfig.getKodo().getAccessKey(), qiniuyunConfig.getKodo().getSecretKey());
+        String upToken = auth.uploadToken(qiniuyunConfig.getKodo().getBucketName(), fileUrl);
+
+        String localTempDir = UFOPUtils.getStaticPath() + "temp";
+
+        try {
+            //设置断点续传文件进度保存目录
+            FileRecorder fileRecorder = new FileRecorder(localTempDir);
+            UploadManager uploadManager = new UploadManager(cfg, fileRecorder);
+            Response response = uploadManager.put(inputStream, fileUrl, upToken, null, null);
+            //解析上传成功的结果
+            DefaultPutRet putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class);
+            log.info(putRet.key);
+            log.info(putRet.hash);
+        } catch (IOException ex) {
+            throw new WriteException("七牛云写文件失败!", ex);
+        }
+
+
+    }
+
+
+}

+ 16 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/util/AliyunUtils.java

@@ -0,0 +1,16 @@
+package com.qiwenshare.ufop.util;
+
+import com.aliyun.oss.OSS;
+import com.aliyun.oss.OSSClientBuilder;
+import com.qiwenshare.ufop.config.AliyunConfig;
+
+public class AliyunUtils {
+
+    public static OSS getOSSClient(AliyunConfig aliyunConfig) {
+        OSS ossClient = new OSSClientBuilder().build(aliyunConfig.getOss().getEndpoint(),
+                aliyunConfig.getOss().getAccessKeyId(),
+                aliyunConfig.getOss().getAccessKeySecret());
+        return ossClient;
+    }
+
+}

+ 119 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/util/CharsetUtils.java

@@ -0,0 +1,119 @@
+package com.qiwenshare.ufop.util;
+
+import com.qiwenshare.ufop.exception.UFOPException;
+
+import java.io.*;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+
+public class CharsetUtils {
+
+    public static byte[] convertTxtCharsetToGBK(byte[] bytes, String extendName) {
+
+        if(Arrays.asList(UFOPUtils.TXT_FILE).contains(extendName)) {
+            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
+            try {
+                String str = new String(bytes, getFileCharsetName(byteArrayInputStream));
+                return str.getBytes("GBK");
+            } catch (IOException e) {
+                throw new UFOPException(e);
+            }
+        }
+        return bytes;
+    }
+
+    public static byte[] convertTxtCharsetToUTF8(byte[] bytes, String extendName) {
+
+        if(Arrays.asList(UFOPUtils.TXT_FILE).contains(extendName)) {
+            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
+            try {
+                String str = new String(bytes, getFileCharsetName(byteArrayInputStream));
+                return str.getBytes(StandardCharsets.UTF_8);
+            } catch (IOException e) {
+                throw new UFOPException(e);
+            }
+        }
+        return bytes;
+    }
+
+
+    public static String getFileCharsetName(InputStream inputStream) {
+
+        String charset = "GBK";
+        byte[] first3Bytes = new byte[3];
+        try {
+            boolean checked = false;
+            BufferedInputStream bis = new BufferedInputStream(inputStream);
+            bis.mark(0); // 读者注: bis.mark(0);修改为 bis.mark(100);我用过这段代码,需要修改上面标出的地方。
+            // Wagsn注:不过暂时使用正常,遂不改之
+            int read = bis.read(first3Bytes, 0, 3);
+            if (read == -1) {
+                bis.close();
+                return charset; // 文件编码为 ANSI
+            } else if (first3Bytes[0] == (byte) 0xFF && first3Bytes[1] == (byte) 0xFE) {
+                charset = "UTF-16LE"; // 文件编码为 Unicode
+                checked = true;
+            } else if (first3Bytes[0] == (byte) 0xFE && first3Bytes[1] == (byte) 0xFF) {
+                charset = "UTF-16BE"; // 文件编码为 Unicode big endian
+                checked = true;
+            } else if (first3Bytes[0] == (byte) 0xEF && first3Bytes[1] == (byte) 0xBB
+                    && first3Bytes[2] == (byte) 0xBF) {
+                charset = "UTF-8"; // 文件编码为 UTF-8
+                checked = true;
+            }
+            bis.reset();
+            if (!checked) {
+                while ((read = bis.read()) != -1) {
+                    if (read >= 0xF0)
+                        break;
+                    if (0x80 <= read && read <= 0xBF) // 单独出现BF以下的,也算是GBK
+                        break;
+                    if (0xC0 <= read && read <= 0xDF) {
+                        read = bis.read();
+                        if (0x80 <= read && read <= 0xBF) // 双字节 (0xC0 - 0xDF)
+                            // (0x80 - 0xBF),也可能在GB编码内
+                        {
+                        }
+                        else
+                            break;
+                    } else if (0xE0 <= read && read <= 0xEF) { // 也有可能出错,但是几率较小
+                        read = bis.read();
+                        if (0x80 <= read && read <= 0xBF) {
+                            read = bis.read();
+                            if (0x80 <= read && read <= 0xBF) {
+                                charset = "UTF-8";
+                            }
+                            break;
+                        } else
+                            break;
+                    }
+                }
+            }
+            bis.close();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+        return charset;
+
+    }
+
+    public static void main(String[] args) {
+        System.out.println(java.nio.charset.Charset.forName("GB2312").newEncoder().canEncode("Îļþ¼ÐѹËõ"));
+        System.out.println(StandardCharsets.ISO_8859_1.newEncoder().canEncode("Îļþ¼ÐѹËõ"));
+        System.out.println(StandardCharsets.UTF_8.newEncoder().canEncode("Îļþ¼ÐѹËõ"));
+        System.out.println(StandardCharsets.US_ASCII.newEncoder().canEncode("Îļþ¼ÐѹËõ"));
+//        System.out.println(StandardCharsets.ISO_8859_1.newEncoder().canEncode("Îļþ¼ÐѹËõ"));
+        byte[] e = "Îļþ¼ÐѹËõ".getBytes(StandardCharsets.ISO_8859_1);
+        try {
+            System.out.println(new String("Îļþ¼ÐѹËõ".getBytes("GBK"), "UTF-8"));
+        } catch (UnsupportedEncodingException ex) {
+            throw new RuntimeException(ex);
+        }
+
+        System.out.println(getFileCharsetName(new ByteArrayInputStream("Îļþ¼ÐѹËõ".getBytes())));
+    }
+
+
+
+}

+ 28 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/util/QiniuyunUtils.java

@@ -0,0 +1,28 @@
+package com.qiwenshare.ufop.util;
+
+import com.qiniu.storage.Configuration;
+import com.qiniu.storage.Region;
+import com.qiwenshare.ufop.config.QiniuyunConfig;
+
+public class QiniuyunUtils {
+    public static Configuration getCfg(QiniuyunConfig qiniuyunConfig) {
+        Region region = null;
+        if ("huadong".equals(qiniuyunConfig.getKodo().getEndpoint())) {
+            region = Region.huadong();
+        } else if ("huanan".equals(qiniuyunConfig.getKodo().getEndpoint())) {
+            region = Region.huanan();
+        } else if ("huabei".equals(qiniuyunConfig.getKodo().getEndpoint())) {
+            region = Region.huabei();
+        } else if ("beimei".equals(qiniuyunConfig.getKodo().getEndpoint())) {
+            region = Region.beimei();
+        } else if ("xinjiapo".equals(qiniuyunConfig.getKodo().getEndpoint())
+                || "dongnanya".equals(qiniuyunConfig.getKodo().getEndpoint())) {
+            region = Region.xinjiapo();
+        }
+        //构造一个带指定 Region 对象的配置类
+        return new Configuration(region);
+    }
+
+
+
+}

+ 214 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/util/ReadFileUtils.java

@@ -0,0 +1,214 @@
+package com.qiwenshare.ufop.util;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.pdfbox.io.RandomAccessReadBuffer;
+import org.apache.pdfbox.pdfparser.PDFParser;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.text.PDFTextStripper;
+import org.apache.poi.hslf.extractor.QuickButCruddyTextExtractor;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.hwpf.extractor.WordExtractor;
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
+import org.apache.poi.openxml4j.opc.OPCPackage;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.xslf.extractor.XSLFExtractor;
+import org.apache.poi.xslf.usermodel.XMLSlideShow;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+import org.apache.poi.xwpf.extractor.XWPFWordExtractor;
+
+import java.io.*;
+
+/**
+ * @author wangshuaijun
+ * 读取文件工具类:支持以下文件内容读取
+ * 1. word(.doc),word(.docx)
+ * 2. excel(.xls),excel(xlsx)
+ * 3. pdf
+ * 4. txt
+ * 5. ppt(.ppt),pptx(,pptx)
+ */
+public class ReadFileUtils {
+    public static void main(String[] args) {
+        try {
+            System.out.println(getContentByInputStream("pdf", new FileInputStream("C:\\Users\\马超\\OneDrive\\个人资料\\马超   入职材料\\养老参保缴费凭证--马超.pdf")));
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 根据文件类型返回文件内容
+     *
+     * @param fileType    文件类型
+     * @param inputStream 输入流
+     * @return 结果
+     * @throws IOException io异常
+     */
+    public static String getContentByInputStream(String fileType, InputStream inputStream) throws IOException {
+        if ("doc".equals(fileType) || "docx".equals(fileType)) {
+            return readWord(inputStream, fileType);
+        } else if ("xlsx".equals(fileType) || "xls".equals(fileType)) {
+            return readExcel(inputStream, fileType);
+        } else if ("txt".equals(fileType)) {
+            return readTxt(inputStream, fileType);
+        } else if ("pdf".equals(fileType)) {
+            return readPdf(inputStream);
+        } else if ("ppt".equals(fileType) || "pptx".equals(fileType)) {
+            return readPPT(inputStream, fileType);
+        } else {
+            System.out.println("不支持的文件类型!");
+        }
+        return "";
+    }
+
+    /**
+     * 读取pdf内容
+     *
+     * @param inputStream 输入流
+     * @return 结果
+     */
+    public static String readPdf(InputStream inputStream) {
+        PDDocument pdDocument = null;
+        String content = "";
+        try {
+            //创建解析器对象
+            PDFParser pdfParser = new PDFParser(new RandomAccessReadBuffer(inputStream));
+            pdDocument = pdfParser.parse();
+            //pdf文档
+//            pdDocument = pdfParser.getPDDocument();
+            //pdf文本操作对象,使用该对象可以获取所读取pdf的一些信息
+            PDFTextStripper pdfTextStripper = new PDFTextStripper();
+            content = pdfTextStripper.getText(pdDocument);
+        } catch (IOException e) {
+            e.printStackTrace();
+        } finally {
+            IOUtils.closeQuietly(inputStream);
+            IOUtils.closeQuietly(pdDocument);
+        }
+        return content;
+    }
+
+    /**
+     * 读取Excel中的内容
+     *
+     * @param extendName 文件路径
+     * @return 返回结果
+     * @throws IOException IOException
+     */
+    private static String readTxt(InputStream inputStream, String extendName) throws IOException {
+        try {
+            byte[] bytes = IOUtils.toByteArray(inputStream);
+            byte[] result = CharsetUtils.convertTxtCharsetToUTF8(bytes, extendName);
+            return IOUtils.toString(result, "UTF-8");
+        } finally {
+            IOUtils.closeQuietly(inputStream);
+        }
+    }
+
+    /**
+     * 读取Excel中的内容
+     *
+     * @param extendName  文件类型
+     * @param inputStream 输入流
+     * @return 结果
+     */
+    private static String readExcel(InputStream inputStream, String extendName) {
+        Workbook wb = null;
+        try {
+            //根据文件后缀(xls/xlsx)进行判断
+            if ("xls".equalsIgnoreCase(extendName)) {
+//                    FileInputStream fis = new FileInputStream(excel);   //文件流对象
+                wb = new HSSFWorkbook(inputStream);
+            } else if ("xlsx".equalsIgnoreCase(extendName)) {
+                wb = new XSSFWorkbook(inputStream);
+            } else {
+                System.out.println("文件类型错误!");
+                return "";
+            }
+            //开始解析,获取页签数
+            StringBuilder sb = new StringBuilder();
+            for (int i = 0; i < wb.getNumberOfSheets(); i++) {
+                Sheet sheet = wb.getSheetAt(i);     //读取sheet
+                sb.append(sheet.getSheetName()).append("_");
+                int firstRowIndex = sheet.getFirstRowNum() + 1;   //第一行是列名,所以不读
+                int lastRowIndex = sheet.getLastRowNum();
+                for (int rIndex = firstRowIndex; rIndex <= lastRowIndex; rIndex++) {   //遍历行
+                    Row row = sheet.getRow(rIndex);
+                    if (row != null) {
+                        int firstCellIndex = row.getFirstCellNum();
+                        int lastCellIndex = row.getLastCellNum();
+                        for (int cIndex = firstCellIndex; cIndex < lastCellIndex; cIndex++) {   //遍历列
+                            Cell cell = row.getCell(cIndex);
+                            if (cell != null) {
+                                sb.append(cell);
+                            }
+                        }
+                    }
+                }
+            }
+            return sb.toString();
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            IOUtils.closeQuietly(wb);
+            IOUtils.closeQuietly(inputStream);
+        }
+        return "";
+    }
+
+    /**
+     * 读取word
+     *
+     * @param fileType    文件类型
+     * @param inputStream 输入流
+     * @return 结果
+     */
+    public static String readWord(InputStream inputStream, String fileType) {
+        String buffer = "";
+        try {
+            if ("doc".equalsIgnoreCase(fileType)) {
+                WordExtractor ex = new WordExtractor(inputStream);
+                buffer = ex.getText();
+                ex.close();
+            } else if ("docx".equalsIgnoreCase(fileType)) {
+                XWPFWordExtractor extractor = new XWPFWordExtractor(OPCPackage.open(inputStream));
+                buffer = extractor.getText();
+                extractor.close();
+
+            } else {
+                System.out.println("此文件不是word文件!");
+            }
+
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            IOUtils.closeQuietly(inputStream);
+        }
+
+        return buffer;
+    }
+
+    private static String readPPT(InputStream inputStream, String fileType) {
+        String buffer = "";
+        try {
+            if ("ppt".equalsIgnoreCase(fileType)) {
+                QuickButCruddyTextExtractor extractor = new QuickButCruddyTextExtractor(inputStream);
+                buffer = extractor.getTextAsString();
+                extractor.close();
+            } else if ("pptx".equalsIgnoreCase(fileType)) {
+                XSLFExtractor extractor = new XSLFExtractor(new XMLSlideShow(OPCPackage.open(inputStream)));
+                buffer = extractor.getText();
+                extractor.close();
+            }
+        } catch (IOException e) {
+            e.fillInStackTrace();
+        } catch (OpenXML4JException e) {
+            e.getMessage();
+        }
+
+        return buffer;
+    }
+}

+ 71 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/util/RedisUtil.java

@@ -0,0 +1,71 @@
+package com.qiwenshare.ufop.util;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import java.util.concurrent.TimeUnit;
+
+@Component
+@Slf4j
+public class RedisUtil {
+
+    @Resource
+    StringRedisTemplate stringRedisTemplate;
+
+    /**
+     * 将值放入缓存
+     * @param key 键
+     * @param value 值
+     */
+    public void set(String key, String value) {
+        stringRedisTemplate.opsForValue().set(key, value);
+    }
+
+    /**
+     * 获取对象
+     * @param key 键
+     * @return 返回值
+     */
+    public String getObject(String key) {
+        return stringRedisTemplate.opsForValue().get(key);
+    }
+
+    /**
+     * 将值放入缓存并设置时间-秒
+     * @param key 键
+     * @param value 值
+     * @param time 时间(单位:秒),如果值为负数,则永久
+     */
+    public void set(String key, String value, long time) {
+        if (time > 0) {
+            stringRedisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
+        } else {
+            stringRedisTemplate.opsForValue().set(key, value);
+        }
+    }
+
+
+    public boolean hasKey(String key) {
+        return stringRedisTemplate.hasKey(key);
+    }
+
+    /**
+     * 删除key
+     * @param key key
+     */
+    public void deleteKey(String key) {
+        stringRedisTemplate.delete(key);
+    }
+
+    /**
+     * 获取自增长值
+     * @param key 键
+     * @return 返回增长之后的值
+     */
+    public Long getIncr(String key) {
+        return stringRedisTemplate.opsForValue().increment(key, 1);
+    }
+
+}

+ 198 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/util/UFOPUtils.java

@@ -0,0 +1,198 @@
+package com.qiwenshare.ufop.util;
+
+import com.qiwenshare.ufop.exception.UFOPException;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.util.ResourceUtils;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+public class UFOPUtils {
+
+    public static String LOCAL_STORAGE_PATH;
+    public static String ROOT_PATH;
+    public static final String[] IMG_FILE = {"bmp", "jpg", "png", "tif", "gif", "jpeg"};
+    public static final String[] DOC_FILE = {"doc", "docx", "ppt", "pptx", "xls", "xlsx", "txt", "hlp", "wps", "rtf", "html", "pdf"};
+    public static final String[] VIDEO_FILE = {"avi", "mp4", "mpg", "mov", "swf"};
+    public static final String[] MUSIC_FILE = {"wav", "aif", "au", "mp3", "ram", "wma", "mmf", "amr", "aac", "flac"};
+    public static final String[] TXT_FILE = {"txt", "html", "java", "xml", "js", "css", "json", "sql"};
+    public static final int IMAGE_TYPE = 1;
+    public static final int DOC_TYPE = 2;
+    public static final int VIDEO_TYPE = 3;
+    public static final int MUSIC_TYPE = 4;
+    public static final int OTHER_TYPE = 5;
+    public static final int SHARE_FILE = 6;
+    public static final int RECYCLE_FILE = 7;
+
+
+    /**
+     * 判断是否为图片文件
+     *
+     * @param extendName 文件扩展名
+     * @return 是否为图片文件
+     */
+    public static boolean isImageFile(String extendName) {
+        for (String extend : IMG_FILE) {
+            if (extendName.equalsIgnoreCase(extend)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 判断是否为视频文件
+     * @param extendName 扩展名
+     * @return 是否为视频文件
+     */
+    public static boolean isVideoFile(String extendName) {
+        for (String extend : VIDEO_FILE) {
+            if (extendName.equalsIgnoreCase(extend)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public static String pathSplitFormat(String filePath) {
+        return filePath.replace("///", "/")
+                .replace("//", "/")
+                .replace("\\\\\\", "/")
+                .replace("\\\\", "/");
+    }
+
+    public static File getLocalSaveFile(String fileUrl) {
+        String localSavePath = UFOPUtils.getStaticPath() + fileUrl;
+        return new File(localSavePath);
+    }
+
+    public static File getCacheFile(String fileUrl) {
+        String cachePath = UFOPUtils.getStaticPath() + "cache" + File.separator + fileUrl;
+
+        return new File(cachePath);
+    }
+
+    public static File getTempFile(String fileUrl) {
+        String tempPath = UFOPUtils.getStaticPath() + "temp" + File.separator + fileUrl;
+        File tempFile = new File(tempPath);
+        File parentFile = tempFile.getParentFile();
+        if (!parentFile.exists()) {
+            parentFile.mkdirs();
+        }
+
+        return tempFile;
+    }
+
+    public static File getProcessFile(String fileUrl) {
+        String processPath = UFOPUtils.getStaticPath() + "temp" + File.separator + "process" + File.separator + fileUrl;
+        File processFile = new File(processPath);
+        File parentFile = processFile.getParentFile();
+        if (!parentFile.exists()) {
+            parentFile.mkdirs();
+        }
+        return processFile;
+    }
+
+    /**
+     * 获取项目所在的根目录路径 resources路径
+     * @return 结果
+     */
+    public static String getProjectRootPath() {
+        String absolutePath;
+        try {
+            String url = ResourceUtils.getURL("classpath:").getPath();
+            absolutePath = urlDecode(new File(url).getAbsolutePath()) + File.separator;
+        } catch (FileNotFoundException e) {
+            throw new UFOPException(e);
+        }
+
+        return absolutePath;
+    }
+
+    /**
+     * 路径解码
+     * @param url url
+     * @return 结果
+     */
+    public static String urlDecode(String url){
+        String decodeUrl;
+        try {
+            decodeUrl = URLDecoder.decode(url, "utf-8");
+        } catch (UnsupportedEncodingException e) {
+            throw new UFOPException("不支持的编码格式", e);
+        }
+        return  decodeUrl;
+    }
+
+    /**
+     * 得到static路径
+     *
+     * @return 结果
+     */
+    public static String getStaticPath() {
+        String localStoragePath = LOCAL_STORAGE_PATH;
+        if (StringUtils.isNotEmpty(localStoragePath)) {
+
+            return new File(localStoragePath).getPath() + File.separator;
+        }else {
+            String projectRootAbsolutePath = getProjectRootPath();
+
+            int index = projectRootAbsolutePath.indexOf("file:");
+            if (index != -1) {
+                projectRootAbsolutePath = projectRootAbsolutePath.substring(0, index);
+            }
+
+            return new File(projectRootAbsolutePath + "static").getPath() + File.separator;
+        }
+
+
+    }
+
+    /**
+     * 获取上传文件路径
+     * 返回路径格式 “upload/yyyyMMdd/”
+     * @param identifier 文件名(一般传入md5或uuid,防止文件名重复)
+     * @param extendName 文件扩展名
+     * @return 返回上传文件路径
+     */
+    public static String getUploadFileUrl(String identifier, String extendName) {
+
+        SimpleDateFormat formater = new SimpleDateFormat("yyyyMMdd");
+        String path = ROOT_PATH + "/" + formater.format(new Date()) + "/";
+
+        File dir = new File(UFOPUtils.getStaticPath() + path);
+
+        if (!dir.exists()) {
+            dir.mkdirs();
+        }
+
+        path = path + identifier + "." + extendName;
+
+        return path;
+    }
+
+    public static String getAliyunObjectNameByFileUrl(String fileUrl) {
+        if (fileUrl.startsWith("/") || fileUrl.startsWith("\\")) {
+            fileUrl = fileUrl.substring(1);
+        }
+        return fileUrl;
+    }
+
+
+    public static String formatPath(String path) {
+        path = UFOPUtils.pathSplitFormat(path);
+        if ("/".equals(path)) {
+            return path;
+        }
+        if (path.endsWith("/")) {
+            int length = path.length();
+            return path.substring(0, length - 1);
+        }
+        return path;
+    }
+
+}

+ 239 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/java/com/qiwenshare/ufop/util/concurrent/locks/RedisLock.java

@@ -0,0 +1,239 @@
+package com.qiwenshare.ufop.util.concurrent.locks;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.data.redis.connection.RedisStringCommands;
+import org.springframework.data.redis.connection.ReturnType;
+import org.springframework.data.redis.core.RedisCallback;
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.data.redis.core.types.Expiration;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * redis锁
+ */
+@Component
+public class RedisLock {
+
+    private static final Logger log = LoggerFactory.getLogger(RedisLock.class);
+
+    /**
+     * 默认轮休获取锁间隔时间, 单位:毫秒
+     */
+    private static final int DEFAULT_ACQUIRE_RESOLUTION_MILLIS = 100;
+
+    private static final String UNLOCK_LUA;
+
+    private static final long LOCK_EXPIRE_TIME = 60 * 5; //获取锁最大5分钟就会过期
+
+
+    @Resource
+    StringRedisTemplate stringRedisTemplate;
+
+    static {
+        UNLOCK_LUA = "if redis.call(\"get\",KEYS[1]) == ARGV[1] " +
+                "then " +
+                "    return redis.call(\"del\",KEYS[1]) " +
+                "else " +
+                "    return 0 " +
+                "end ";
+    }
+
+    private final ThreadLocal<Map<String, LockVO>> lockMap = new ThreadLocal<>();
+
+    /**
+     * 获取锁,没有获取到则一直等待
+     * @param key 键
+     */
+    public void lock(final String key) {
+
+        try {
+            acquireLock(key, LOCK_EXPIRE_TIME, -1);
+        } catch (Exception e) {
+            throw new RuntimeException("acquire lock exception", e);
+        }
+    }
+
+    /**
+     * 释放锁
+     * @param key 键
+     */
+    public void unlock(String key) {
+        try {
+            release(key);
+        } catch (Exception e) {
+            throw new RuntimeException("release lock exception", e);
+        }
+    }
+
+    /**
+     * 尝试获取锁,指定时间内没有获取到,返回false。否则 返回true
+     * @param key 键
+     * @return 返回是否获取成功
+     */
+    public boolean tryLock(final String key) {
+        try {
+            return acquireLock(key, LOCK_EXPIRE_TIME, -1);
+        } catch (Exception e) {
+            throw new RuntimeException("acquire lock exception", e);
+        }
+    }
+
+    /**
+     * 获取锁,指定时间内没有获取到,返回false。否则 返回true
+     * @param key 键
+     * @param time 获取锁等待时间
+     * @param unit 时间单位
+     * @return 返回是否获取成功
+     */
+    public boolean tryLock(String key, long time, TimeUnit unit) {
+        try {
+            return acquireLock(key, LOCK_EXPIRE_TIME, unit.toSeconds(time));
+        } catch (Exception e) {
+            throw new RuntimeException("acquire lock exception", e);
+        }
+    }
+
+    /**
+     * 获取锁
+     * @param key redis key
+     * @param expire 锁过期时间, 单位 秒
+     * @param waitTime 获取锁超时时间, -1代表永不超时, 单位 秒
+     * @return if true success else fail
+     * @throws InterruptedException 阻塞方法收到中断请求
+     */
+    private boolean acquireLock(String key, long expire, long waitTime) throws InterruptedException {
+        //如果之前获取到了并且没有超时,则返回获取成功
+        boolean acquired = acquired(key);
+        if (acquired) {
+            return true;
+        }
+        long acquireTime = waitTime == -1 ? -1 : waitTime * 1000 + System.currentTimeMillis();
+        //同一个进程,对于同一个key锁,只允许先到的去尝试获取。
+        // key.intern() 如果常量池中存在当前字符串, 就会直接返回当前字符串.
+        // 如果常量池中没有此字符串, 会将此字符串放入常量池中后, 再返回
+        synchronized (key.intern()) {
+            String lockId = UUID.randomUUID().toString();
+            do {
+                long before = System.currentTimeMillis();
+                boolean hasLock = tryLock(key, expire, lockId);
+                //获取锁成功
+                if (hasLock) {
+                    long after = System.currentTimeMillis();
+                    Map<String, LockVO> map = lockMap.get();
+                    if (map == null) {
+                        map = new HashMap<>(2);
+                        lockMap.set(map);
+                    }
+                    map.put(key, new LockVO(1, lockId, expire * 1000 + before, expire * 1000 + after));
+                    log.debug("acquire lock {} {} ", key, 1);
+                    return true;
+                }
+                Thread.sleep(DEFAULT_ACQUIRE_RESOLUTION_MILLIS);
+            } while (acquireTime == -1 || acquireTime > System.currentTimeMillis());
+        }
+        log.info("acquire lock {} fail,because timeout ", key);
+        return false;
+    }
+
+    /**
+     * 释放锁
+     * @param key 键
+     */
+    private void release(String key) {
+        Map<String, LockVO> map = lockMap.get();
+        if (map == null || map.size() == 0 || !map.containsKey(key)) {
+            return;
+        }
+        LockVO vo = map.get(key);
+        if (vo.afterExpireTime < System.currentTimeMillis()) {
+            log.debug("release lock {}, because timeout ", key);
+            map.remove(key);
+            return;
+        }
+        int after = --vo.count;
+        log.debug("release lock {} {} ", key, after);
+        if (after > 0) {
+            return;
+        }
+        map.remove(key);
+        RedisCallback<Boolean> callback = (connection) ->
+            connection.eval(UNLOCK_LUA.getBytes(StandardCharsets.UTF_8), ReturnType.BOOLEAN, 1,
+                (key).getBytes(StandardCharsets.UTF_8), vo.lockId.getBytes(StandardCharsets.UTF_8));
+        stringRedisTemplate.execute(callback);
+    }
+
+    /**
+     * 获取锁
+     * @param key 锁的key
+     * @param expire 锁的超时时间 秒
+     * @param lockId 获取锁后,UUID生成的唯一ID
+     * @return 返回成功或失败
+     */
+    private boolean tryLock(String key, long expire, String lockId) {
+        try{
+            RedisCallback<Boolean> callback = (connection) ->
+                connection.set(
+                        (key).getBytes(StandardCharsets.UTF_8),
+                        lockId.getBytes(StandardCharsets.UTF_8),
+                        Expiration.seconds(expire),
+                        RedisStringCommands.SetOption.SET_IF_ABSENT);
+            return stringRedisTemplate.execute(callback);
+        } catch (Exception e) {
+            log.error("redis lock error.", e);
+        }
+        return false;
+    }
+
+    private static class LockVO {
+        /**
+         * 锁重入的次数
+         */
+        private int count;
+
+        /**
+         * 获取锁后,UUID生成的唯一ID
+         */
+        private String lockId;
+        /**
+         * 获取锁之前的时间戳
+         */
+        private long beforeExpireTime;
+        /**
+         * 获取到锁的时间戳
+         */
+        private long afterExpireTime;
+
+        LockVO(int count, String lockId, long beforeExpireTime, long afterExpireTime) {
+            this.count = count;
+            this.lockId = lockId;
+            this.beforeExpireTime = beforeExpireTime;
+            this.afterExpireTime = afterExpireTime;
+        }
+    }
+
+    private boolean acquired(String key) {
+        Map<String, LockVO> map = lockMap.get();
+        if (map == null || map.size() == 0 || !map.containsKey(key)) {
+            return false;
+        }
+
+        LockVO vo = map.get(key);
+        if (vo.beforeExpireTime < System.currentTimeMillis()) {
+            log.debug("lock {} maybe release, because timeout ", key);
+            return false;
+        }
+        int after = ++vo.count;
+        log.debug("acquire lock {} {} ", key, after);
+        return true;
+    }
+
+}
+

+ 2 - 0
snowy-plugin/snowy-plugin-ufop/ufop-spring-boot-starter/src/main/resources/META-INF/spring.factories

@@ -0,0 +1,2 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+  com.qiwenshare.ufop.autoconfiguration.UFOPAutoConfiguration