qirong
2023-12-22 ac2873bd37eeb496b0e9dd62d66e9fc4b38ef39b
ftp摆渡
14个文件已修改
6个文件已添加
2296 ■■■■ 已修改文件
ruoyi-admin/src/main/java/org/dromara/DromaraApplication.java 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/org/dromara/web/service/SysRegisterService.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/resources/application-prod.yml 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/entity/SynchronousRequest2.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/config/RedisConfig.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/QueueUtils.java 464 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/queue/BoundedQueueController.java 184 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/queue/PriorityQueueController.java 178 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/pom.xml 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SynchronizationController.java 115 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysOssController.java 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/properties/FtpConfig.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/runner/SystemApplicationRunner.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysOssService.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssServiceImpl.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/sync/VideoPulSync.java 117 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/sync/deleteSync.java 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/uitil/FTPUtil.java 547 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/uitil/FtpApche.java 193 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/uitil/HttpUtils.java 162 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/org/dromara/DromaraApplication.java
@@ -3,6 +3,7 @@
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup;
import org.springframework.scheduling.annotation.EnableScheduling;
/**
 * 启动程序
@@ -10,6 +11,7 @@
 * @author Lion Li
 */
@EnableScheduling
@SpringBootApplication
public class DromaraApplication {
@@ -17,7 +19,7 @@
        SpringApplication application = new SpringApplication(DromaraApplication.class);
        application.setApplicationStartup(new BufferingApplicationStartup(2048));
        application.run(args);
        System.out.println("(♥◠‿◠)ノ゙  RuoYi-Vue-Plus启动成功   ლ(´ڡ`ლ)゙");
        System.out.println("(♥◠‿◠)ノ゙  Vue-Plus启动成功   ლ(´ڡ`ლ)゙");
    }
}
ruoyi-admin/src/main/java/org/dromara/web/service/SysRegisterService.java
@@ -78,7 +78,7 @@
        SysUserVo sysUserVo = userMapper.selectUserByUserName(username);
        SysUserRole userRole = new SysUserRole();
        userRole.setUserId(sysUserVo.getUserId());
        userRole.setRoleId(Long.valueOf(RoleType.VIDEO.getType()));
        userRole.setRoleId(Long.valueOf(RoleType.POLICE.getType()));
        userRoleMapper.insert(userRole);
        recordLogininfor(tenantId, username, Constants.REGISTER, MessageUtils.message("user.register.success"));
    }
ruoyi-admin/src/main/resources/application-prod.yml
@@ -43,12 +43,12 @@
          driverClassName: com.mysql.cj.jdbc.Driver
          # jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562
          # rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题)
          url: jdbc:mysql://123.207.71.245:13306/qysp?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true
          username: root
          password: 234#Wersdf!
#          url: jdbc:mysql://80.36.32.176:3306/qysp?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true
#          url: jdbc:mysql://123.207.71.245:13306/qysp?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true
#          username: root
#          password: 234#Wersdf!
          url: jdbc:mysql://80.36.32.176:3306/qysp?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true
          username: root
          password: 234#Wersdf!
#          url: jdbc:mysql://51.9.57.211:3306/qysp?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true
#          username: root
#          password: 234#Wersdf!
@@ -105,9 +105,9 @@
spring.data:
  redis:
    # 地址
    host: 162.14.79.111
    host: 80.36.32.176
    # 端口,默认为6379
    port: 16379
    port: 6379
    password: 234#Wersdf!
    # 数据库索引
    database: 0
@@ -136,7 +136,7 @@
    # 连接空闲超时,单位:毫秒
    idleConnectionTimeout: 10000
    # 命令等待超时,单位:毫秒
    timeout: 3000
    timeout: 10000
    # 发布和订阅连接池大小
    subscriptionConnectionPoolSize: 50
@@ -262,6 +262,13 @@
--- #边界
police:
  localhost: http://192.168.3.228:8801 #测试
  localhost: http://127.0.0.1:8080 #测试
  chief: http://10.248.255.130:18080   #政务云
  video: http://51.9.1.41:18080        #视频网
--- #ftp
ftp:
  url: 51.9.57.211
  username: admin          #本机:ftpup
  passwd: 234#Wersdf!
  path: /
ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/entity/SynchronousRequest2.java
New file
@@ -0,0 +1,25 @@
package org.dromara.common.oss.entity;
import lombok.Data;
import org.springframework.web.multipart.MultipartFile;
import java.io.InputStream;
@Data
public class SynchronousRequest2 {
    //InputStream 流
    private InputStream input;
    //minio路径
    private String path;
    //ossId
    private String ossId;
    //创建时间
    private String createTime;
    //创建人
    private String createBy;
    //密码
    private String password;
    //文件名称
    private String fileName;
}
ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/config/RedisConfig.java
@@ -62,7 +62,7 @@
                    .setSubscriptionConnectionPoolSize(singleServerConfig.getSubscriptionConnectionPoolSize())
                    .setConnectionMinimumIdleSize(singleServerConfig.getConnectionMinimumIdleSize())
                    .setConnectionPoolSize(singleServerConfig.getConnectionPoolSize())
                    .setSubscriptionsPerConnection(5000);
                    .setSubscriptionsPerConnection(60 * 10000);
            }
            // 集群配置方式 参考下方注释
            RedissonProperties.ClusterServersConfig clusterServersConfig = redissonProperties.getClusterServersConfig();
ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/QueueUtils.java
@@ -1,232 +1,232 @@
package org.dromara.common.redis.utils;
import org.dromara.common.core.utils.SpringUtils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.redisson.api.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
/**
 * 分布式队列工具
 * 轻量级队列 重量级数据量 请使用 MQ
 * 要求 redis 5.X 以上
 *
 * @author Lion Li
 * @version 3.6.0 新增
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class QueueUtils {
    private static final RedissonClient CLIENT = SpringUtils.getBean(RedissonClient.class);
    /**
     * 获取客户端实例
     */
    public static RedissonClient getClient() {
        return CLIENT;
    }
    /**
     * 添加普通队列数据
     *
     * @param queueName 队列名
     * @param data      数据
     */
    public static <T> boolean addQueueObject(String queueName, T data) {
        RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);
        return queue.offer(data);
    }
    /**
     * 通用获取一个队列数据 没有数据返回 null(不支持延迟队列)
     *
     * @param queueName 队列名
     */
    public static <T> T getQueueObject(String queueName) {
        RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);
        return queue.poll();
    }
    /**
     * 通用删除队列数据(不支持延迟队列)
     */
    public static <T> boolean removeQueueObject(String queueName, T data) {
        RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);
        return queue.remove(data);
    }
    /**
     * 通用销毁队列 所有阻塞监听 报错(不支持延迟队列)
     */
    public static <T> boolean destroyQueue(String queueName) {
        RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);
        return queue.delete();
    }
    /**
     * 添加延迟队列数据 默认毫秒
     *
     * @param queueName 队列名
     * @param data      数据
     * @param time      延迟时间
     */
    public static <T> void addDelayedQueueObject(String queueName, T data, long time) {
        addDelayedQueueObject(queueName, data, time, TimeUnit.MILLISECONDS);
    }
    /**
     * 添加延迟队列数据
     *
     * @param queueName 队列名
     * @param data      数据
     * @param time      延迟时间
     * @param timeUnit  单位
     */
    public static <T> void addDelayedQueueObject(String queueName, T data, long time, TimeUnit timeUnit) {
        RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);
        RDelayedQueue<T> delayedQueue = CLIENT.getDelayedQueue(queue);
        delayedQueue.offer(data, time, timeUnit);
    }
    /**
     * 获取一个延迟队列数据 没有数据返回 null
     *
     * @param queueName 队列名
     */
    public static <T> T getDelayedQueueObject(String queueName) {
        RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);
        RDelayedQueue<T> delayedQueue = CLIENT.getDelayedQueue(queue);
        return delayedQueue.poll();
    }
    /**
     * 删除延迟队列数据
     */
    public static <T> boolean removeDelayedQueueObject(String queueName, T data) {
        RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);
        RDelayedQueue<T> delayedQueue = CLIENT.getDelayedQueue(queue);
        return delayedQueue.remove(data);
    }
    /**
     * 销毁延迟队列 所有阻塞监听 报错
     */
    public static <T> void destroyDelayedQueue(String queueName) {
        RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);
        RDelayedQueue<T> delayedQueue = CLIENT.getDelayedQueue(queue);
        delayedQueue.destroy();
    }
    /**
     * 添加优先队列数据
     *
     * @param queueName 队列名
     * @param data      数据
     */
    public static <T> boolean addPriorityQueueObject(String queueName, T data) {
        RPriorityBlockingQueue<T> priorityBlockingQueue = CLIENT.getPriorityBlockingQueue(queueName);
        return priorityBlockingQueue.offer(data);
    }
    /**
     * 优先队列获取一个队列数据 没有数据返回 null(不支持延迟队列)
     *
     * @param queueName 队列名
     */
    public static <T> T getPriorityQueueObject(String queueName) {
        RPriorityBlockingQueue<T> queue = CLIENT.getPriorityBlockingQueue(queueName);
        return queue.poll();
    }
    /**
     * 优先队列删除队列数据(不支持延迟队列)
     */
    public static <T> boolean removePriorityQueueObject(String queueName, T data) {
        RPriorityBlockingQueue<T> queue = CLIENT.getPriorityBlockingQueue(queueName);
        return queue.remove(data);
    }
    /**
     * 优先队列销毁队列 所有阻塞监听 报错(不支持延迟队列)
     */
    public static <T> boolean destroyPriorityQueue(String queueName) {
        RPriorityBlockingQueue<T> queue = CLIENT.getPriorityBlockingQueue(queueName);
        return queue.delete();
    }
    /**
     * 尝试设置 有界队列 容量 用于限制数量
     *
     * @param queueName 队列名
     * @param capacity  容量
     */
    public static <T> boolean trySetBoundedQueueCapacity(String queueName, int capacity) {
        RBoundedBlockingQueue<T> boundedBlockingQueue = CLIENT.getBoundedBlockingQueue(queueName);
        return boundedBlockingQueue.trySetCapacity(capacity);
    }
    /**
     * 尝试设置 有界队列 容量 用于限制数量
     *
     * @param queueName 队列名
     * @param capacity  容量
     * @param destroy   已存在是否销毁
     */
    public static <T> boolean trySetBoundedQueueCapacity(String queueName, int capacity, boolean destroy) {
        RBoundedBlockingQueue<T> boundedBlockingQueue = CLIENT.getBoundedBlockingQueue(queueName);
        if (boundedBlockingQueue.isExists() && destroy) {
            destroyQueue(queueName);
        }
        return boundedBlockingQueue.trySetCapacity(capacity);
    }
    /**
     * 添加有界队列数据
     *
     * @param queueName 队列名
     * @param data      数据
     * @return 添加成功 true 已达到界限 false
     */
    public static <T> boolean addBoundedQueueObject(String queueName, T data) {
        RBoundedBlockingQueue<T> boundedBlockingQueue = CLIENT.getBoundedBlockingQueue(queueName);
        return boundedBlockingQueue.offer(data);
    }
    /**
     * 有界队列获取一个队列数据 没有数据返回 null(不支持延迟队列)
     *
     * @param queueName 队列名
     */
    public static <T> T getBoundedQueueObject(String queueName) {
        RBoundedBlockingQueue<T> queue = CLIENT.getBoundedBlockingQueue(queueName);
        return queue.poll();
    }
    /**
     * 有界队列删除队列数据(不支持延迟队列)
     */
    public static <T> boolean removeBoundedQueueObject(String queueName, T data) {
        RBoundedBlockingQueue<T> queue = CLIENT.getBoundedBlockingQueue(queueName);
        return queue.remove(data);
    }
    /**
     * 有界队列销毁队列 所有阻塞监听 报错(不支持延迟队列)
     */
    public static <T> boolean destroyBoundedQueue(String queueName) {
        RBoundedBlockingQueue<T> queue = CLIENT.getBoundedBlockingQueue(queueName);
        return queue.delete();
    }
    /**
     * 订阅阻塞队列(可订阅所有实现类 例如: 延迟 优先 有界 等)
     */
    public static <T> void subscribeBlockingQueue(String queueName, Consumer<T> consumer) {
        RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);
        queue.subscribeOnElements(consumer);
    }
}
//package org.dromara.common.redis.utils;
//
//import org.dromara.common.core.utils.SpringUtils;
//import lombok.AccessLevel;
//import lombok.NoArgsConstructor;
//import org.redisson.api.*;
//
//import java.util.concurrent.TimeUnit;
//import java.util.function.Consumer;
//
///**
// * 分布式队列工具
// * 轻量级队列 重量级数据量 请使用 MQ
// * 要求 redis 5.X 以上
// *
// * @author Lion Li
// * @version 3.6.0 新增
// */
//@NoArgsConstructor(access = AccessLevel.PRIVATE)
//public class QueueUtils {
//
//    private static final RedissonClient CLIENT = SpringUtils.getBean(RedissonClient.class);
//
//
//    /**
//     * 获取客户端实例
//     */
//    public static RedissonClient getClient() {
//        return CLIENT;
//    }
//
//    /**
//     * 添加普通队列数据
//     *
//     * @param queueName 队列名
//     * @param data      数据
//     */
//    public static <T> boolean addQueueObject(String queueName, T data) {
//        RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);
//        return queue.offer(data);
//    }
//
//    /**
//     * 通用获取一个队列数据 没有数据返回 null(不支持延迟队列)
//     *
//     * @param queueName 队列名
//     */
//    public static <T> T getQueueObject(String queueName) {
//        RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);
//        return queue.poll();
//    }
//
//    /**
//     * 通用删除队列数据(不支持延迟队列)
//     */
//    public static <T> boolean removeQueueObject(String queueName, T data) {
//        RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);
//        return queue.remove(data);
//    }
//
//    /**
//     * 通用销毁队列 所有阻塞监听 报错(不支持延迟队列)
//     */
//    public static <T> boolean destroyQueue(String queueName) {
//        RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);
//        return queue.delete();
//    }
//
//    /**
//     * 添加延迟队列数据 默认毫秒
//     *
//     * @param queueName 队列名
//     * @param data      数据
//     * @param time      延迟时间
//     */
//    public static <T> void addDelayedQueueObject(String queueName, T data, long time) {
//        addDelayedQueueObject(queueName, data, time, TimeUnit.MILLISECONDS);
//    }
//
//    /**
//     * 添加延迟队列数据
//     *
//     * @param queueName 队列名
//     * @param data      数据
//     * @param time      延迟时间
//     * @param timeUnit  单位
//     */
//    public static <T> void addDelayedQueueObject(String queueName, T data, long time, TimeUnit timeUnit) {
//        RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);
//        RDelayedQueue<T> delayedQueue = CLIENT.getDelayedQueue(queue);
//        delayedQueue.offer(data, time, timeUnit);
//    }
//
//    /**
//     * 获取一个延迟队列数据 没有数据返回 null
//     *
//     * @param queueName 队列名
//     */
//    public static <T> T getDelayedQueueObject(String queueName) {
//        RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);
//        RDelayedQueue<T> delayedQueue = CLIENT.getDelayedQueue(queue);
//        return delayedQueue.poll();
//    }
//
//    /**
//     * 删除延迟队列数据
//     */
//    public static <T> boolean removeDelayedQueueObject(String queueName, T data) {
//        RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);
//        RDelayedQueue<T> delayedQueue = CLIENT.getDelayedQueue(queue);
//        return delayedQueue.remove(data);
//    }
//
//    /**
//     * 销毁延迟队列 所有阻塞监听 报错
//     */
//    public static <T> void destroyDelayedQueue(String queueName) {
//        RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);
//        RDelayedQueue<T> delayedQueue = CLIENT.getDelayedQueue(queue);
//        delayedQueue.destroy();
//    }
//
//    /**
//     * 添加优先队列数据
//     *
//     * @param queueName 队列名
//     * @param data      数据
//     */
//    public static <T> boolean addPriorityQueueObject(String queueName, T data) {
//        RPriorityBlockingQueue<T> priorityBlockingQueue = CLIENT.getPriorityBlockingQueue(queueName);
//        return priorityBlockingQueue.offer(data);
//    }
//
//    /**
//     * 优先队列获取一个队列数据 没有数据返回 null(不支持延迟队列)
//     *
//     * @param queueName 队列名
//     */
//    public static <T> T getPriorityQueueObject(String queueName) {
//        RPriorityBlockingQueue<T> queue = CLIENT.getPriorityBlockingQueue(queueName);
//        return queue.poll();
//    }
//
//    /**
//     * 优先队列删除队列数据(不支持延迟队列)
//     */
//    public static <T> boolean removePriorityQueueObject(String queueName, T data) {
//        RPriorityBlockingQueue<T> queue = CLIENT.getPriorityBlockingQueue(queueName);
//        return queue.remove(data);
//    }
//
//    /**
//     * 优先队列销毁队列 所有阻塞监听 报错(不支持延迟队列)
//     */
//    public static <T> boolean destroyPriorityQueue(String queueName) {
//        RPriorityBlockingQueue<T> queue = CLIENT.getPriorityBlockingQueue(queueName);
//        return queue.delete();
//    }
//
//    /**
//     * 尝试设置 有界队列 容量 用于限制数量
//     *
//     * @param queueName 队列名
//     * @param capacity  容量
//     */
//    public static <T> boolean trySetBoundedQueueCapacity(String queueName, int capacity) {
//        RBoundedBlockingQueue<T> boundedBlockingQueue = CLIENT.getBoundedBlockingQueue(queueName);
//        return boundedBlockingQueue.trySetCapacity(capacity);
//    }
//
//    /**
//     * 尝试设置 有界队列 容量 用于限制数量
//     *
//     * @param queueName 队列名
//     * @param capacity  容量
//     * @param destroy   已存在是否销毁
//     */
//    public static <T> boolean trySetBoundedQueueCapacity(String queueName, int capacity, boolean destroy) {
//        RBoundedBlockingQueue<T> boundedBlockingQueue = CLIENT.getBoundedBlockingQueue(queueName);
//        if (boundedBlockingQueue.isExists() && destroy) {
//            destroyQueue(queueName);
//        }
//        return boundedBlockingQueue.trySetCapacity(capacity);
//    }
//
//    /**
//     * 添加有界队列数据
//     *
//     * @param queueName 队列名
//     * @param data      数据
//     * @return 添加成功 true 已达到界限 false
//     */
//    public static <T> boolean addBoundedQueueObject(String queueName, T data) {
//        RBoundedBlockingQueue<T> boundedBlockingQueue = CLIENT.getBoundedBlockingQueue(queueName);
//        return boundedBlockingQueue.offer(data);
//    }
//
//    /**
//     * 有界队列获取一个队列数据 没有数据返回 null(不支持延迟队列)
//     *
//     * @param queueName 队列名
//     */
//    public static <T> T getBoundedQueueObject(String queueName) {
//        RBoundedBlockingQueue<T> queue = CLIENT.getBoundedBlockingQueue(queueName);
//        return queue.poll();
//    }
//
//    /**
//     * 有界队列删除队列数据(不支持延迟队列)
//     */
//    public static <T> boolean removeBoundedQueueObject(String queueName, T data) {
//        RBoundedBlockingQueue<T> queue = CLIENT.getBoundedBlockingQueue(queueName);
//        return queue.remove(data);
//    }
//
//    /**
//     * 有界队列销毁队列 所有阻塞监听 报错(不支持延迟队列)
//     */
//    public static <T> boolean destroyBoundedQueue(String queueName) {
//        RBoundedBlockingQueue<T> queue = CLIENT.getBoundedBlockingQueue(queueName);
//        return queue.delete();
//    }
//
//    /**
//     * 订阅阻塞队列(可订阅所有实现类 例如: 延迟 优先 有界 等)
//     */
//    public static <T> void subscribeBlockingQueue(String queueName, Consumer<T> consumer) {
//        RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);
//        queue.subscribeOnElements(consumer);
//    }
//
//}
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/queue/BoundedQueueController.java
@@ -1,92 +1,92 @@
package org.dromara.demo.controller.queue;
import cn.dev33.satoken.annotation.SaIgnore;
import org.dromara.common.core.domain.R;
import org.dromara.common.redis.utils.QueueUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBoundedBlockingQueue;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
 * 有界队列 演示案例
 * <p>
 * 轻量级队列 重量级数据量 请使用 MQ
 * <p>
 * 集群测试通过 同一个数据只会被消费一次 做好事务补偿
 * 集群测试流程 在其中一台发送数据 两端分别调用获取接口 一次获取一条
 *
 * @author Lion Li
 * @version 3.6.0
 */
@Slf4j
@RequiredArgsConstructor
@RestController
@RequestMapping("/demo/queue/bounded")
public class BoundedQueueController {
    /**
     * 添加队列数据
     *
     * @param queueName 队列名
     * @param capacity  容量
     */
    @GetMapping("/add")
    public R<Void> add(String queueName, int capacity) {
        // 用完了一定要销毁 否则会一直存在
        boolean b = QueueUtils.destroyBoundedQueue(queueName);
        log.info("通道: {} , 删除: {}", queueName, b);
        // 初始化设置一次即可
        if (QueueUtils.trySetBoundedQueueCapacity(queueName, capacity)) {
            log.info("通道: {} , 设置容量: {}", queueName, capacity);
        } else {
            log.info("通道: {} , 设置容量失败", queueName);
            return R.fail("操作失败");
        }
        for (int i = 0; i < 11; i++) {
            String data = "data-" + i;
            boolean flag = QueueUtils.addBoundedQueueObject(queueName, data);
            if (flag == false) {
                log.info("通道: {} , 发送数据: {} 失败, 通道已满", queueName, data);
            } else {
                log.info("通道: {} , 发送数据: {}", queueName, data);
            }
        }
        return R.ok("操作成功");
    }
    /**
     * 删除队列数据
     *
     * @param queueName 队列名
     */
    @GetMapping("/remove")
    public R<Void> remove(String queueName) {
        String data = "data-" + 5;
        if (QueueUtils.removeBoundedQueueObject(queueName, data)) {
            log.info("通道: {} , 删除数据: {}", queueName, data);
        } else {
            return R.fail("操作失败");
        }
        return R.ok("操作成功");
    }
    /**
     * 获取队列数据
     *
     * @param queueName 队列名
     */
    @GetMapping("/get")
    public R<Void> get(String queueName) {
        String data;
        do {
            data = QueueUtils.getBoundedQueueObject(queueName);
            log.info("通道: {} , 获取数据: {}", queueName, data);
        } while (data != null);
        return R.ok("操作成功");
    }
}
//package org.dromara.demo.controller.queue;
//
//import cn.dev33.satoken.annotation.SaIgnore;
//import org.dromara.common.core.domain.R;
//import org.dromara.common.redis.utils.QueueUtils;
//import lombok.RequiredArgsConstructor;
//import lombok.extern.slf4j.Slf4j;
//import org.redisson.api.RBoundedBlockingQueue;
//import org.springframework.web.bind.annotation.GetMapping;
//import org.springframework.web.bind.annotation.RequestMapping;
//import org.springframework.web.bind.annotation.RestController;
//
///**
// * 有界队列 演示案例
// * <p>
// * 轻量级队列 重量级数据量 请使用 MQ
// * <p>
// * 集群测试通过 同一个数据只会被消费一次 做好事务补偿
// * 集群测试流程 在其中一台发送数据 两端分别调用获取接口 一次获取一条
// *
// * @author Lion Li
// * @version 3.6.0
// */
//@Slf4j
//@RequiredArgsConstructor
//@RestController
//@RequestMapping("/demo/queue/bounded")
//public class BoundedQueueController {
//
//
//    /**
//     * 添加队列数据
//     *
//     * @param queueName 队列名
//     * @param capacity  容量
//     */
//    @GetMapping("/add")
//    public R<Void> add(String queueName, int capacity) {
//        // 用完了一定要销毁 否则会一直存在
//        boolean b = QueueUtils.destroyBoundedQueue(queueName);
//        log.info("通道: {} , 删除: {}", queueName, b);
//        // 初始化设置一次即可
//        if (QueueUtils.trySetBoundedQueueCapacity(queueName, capacity)) {
//            log.info("通道: {} , 设置容量: {}", queueName, capacity);
//        } else {
//            log.info("通道: {} , 设置容量失败", queueName);
//            return R.fail("操作失败");
//        }
//        for (int i = 0; i < 11; i++) {
//            String data = "data-" + i;
//            boolean flag = QueueUtils.addBoundedQueueObject(queueName, data);
//            if (flag == false) {
//                log.info("通道: {} , 发送数据: {} 失败, 通道已满", queueName, data);
//            } else {
//                log.info("通道: {} , 发送数据: {}", queueName, data);
//            }
//        }
//        return R.ok("操作成功");
//    }
//
//    /**
//     * 删除队列数据
//     *
//     * @param queueName 队列名
//     */
//    @GetMapping("/remove")
//    public R<Void> remove(String queueName) {
//        String data = "data-" + 5;
//        if (QueueUtils.removeBoundedQueueObject(queueName, data)) {
//            log.info("通道: {} , 删除数据: {}", queueName, data);
//        } else {
//            return R.fail("操作失败");
//        }
//        return R.ok("操作成功");
//    }
//
//    /**
//     * 获取队列数据
//     *
//     * @param queueName 队列名
//     */
//    @GetMapping("/get")
//    public R<Void> get(String queueName) {
//        String data;
//        do {
//            data = QueueUtils.getBoundedQueueObject(queueName);
//            log.info("通道: {} , 获取数据: {}", queueName, data);
//        } while (data != null);
//        return R.ok("操作成功");
//    }
//
//}
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/queue/PriorityQueueController.java
@@ -1,89 +1,89 @@
package org.dromara.demo.controller.queue;
import cn.hutool.core.util.RandomUtil;
import org.dromara.common.core.domain.R;
import org.dromara.common.redis.utils.QueueUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
 * 优先队列 演示案例
 * <p>
 * 轻量级队列 重量级数据量 请使用 MQ
 * <p>
 * 集群测试通过 同一个消息只会被消费一次 做好事务补偿
 * 集群测试流程 在其中一台发送数据 两端分别调用获取接口 一次获取一条
 *
 * @author Lion Li
 * @version 3.6.0
 */
@Slf4j
@RequiredArgsConstructor
@RestController
@RequestMapping("/demo/queue/priority")
public class PriorityQueueController {
    /**
     * 添加队列数据
     *
     * @param queueName 队列名
     */
    @GetMapping("/add")
    public R<Void> add(String queueName) {
        // 用完了一定要销毁 否则会一直存在
        boolean b = QueueUtils.destroyPriorityQueue(queueName);
        log.info("通道: {} , 删除: {}", queueName, b);
        for (int i = 0; i < 10; i++) {
            int randomNum = RandomUtil.randomInt(10);
            PriorityDemo data = new PriorityDemo();
            data.setName("data-" + i);
            data.setOrderNum(randomNum);
            if (QueueUtils.addPriorityQueueObject(queueName, data)) {
                log.info("通道: {} , 发送数据: {}", queueName, data);
            } else {
                log.info("通道: {} , 发送数据: {}, 发送失败", queueName, data);
            }
        }
        return R.ok("操作成功");
    }
    /**
     * 删除队列数据
     *
     * @param queueName 队列名
     * @param name      对象名
     * @param orderNum  排序号
     */
    @GetMapping("/remove")
    public R<Void> remove(String queueName, String name, Integer orderNum) {
        PriorityDemo data = new PriorityDemo();
        data.setName(name);
        data.setOrderNum(orderNum);
        if (QueueUtils.removePriorityQueueObject(queueName, data)) {
            log.info("通道: {} , 删除数据: {}", queueName, data);
        } else {
            return R.fail("操作失败");
        }
        return R.ok("操作成功");
    }
    /**
     * 获取队列数据
     *
     * @param queueName 队列名
     */
    @GetMapping("/get")
    public R<Void> get(String queueName) {
        PriorityDemo data;
        do {
            data = QueueUtils.getPriorityQueueObject(queueName);
            log.info("通道: {} , 获取数据: {}", queueName, data);
        } while (data != null);
        return R.ok("操作成功");
    }
}
//package org.dromara.demo.controller.queue;
//
//import cn.hutool.core.util.RandomUtil;
//import org.dromara.common.core.domain.R;
//import org.dromara.common.redis.utils.QueueUtils;
//import lombok.RequiredArgsConstructor;
//import lombok.extern.slf4j.Slf4j;
//import org.springframework.web.bind.annotation.GetMapping;
//import org.springframework.web.bind.annotation.RequestMapping;
//import org.springframework.web.bind.annotation.RestController;
//
///**
// * 优先队列 演示案例
// * <p>
// * 轻量级队列 重量级数据量 请使用 MQ
// * <p>
// * 集群测试通过 同一个消息只会被消费一次 做好事务补偿
// * 集群测试流程 在其中一台发送数据 两端分别调用获取接口 一次获取一条
// *
// * @author Lion Li
// * @version 3.6.0
// */
//@Slf4j
//@RequiredArgsConstructor
//@RestController
//@RequestMapping("/demo/queue/priority")
//public class PriorityQueueController {
//
//    /**
//     * 添加队列数据
//     *
//     * @param queueName 队列名
//     */
//    @GetMapping("/add")
//    public R<Void> add(String queueName) {
//        // 用完了一定要销毁 否则会一直存在
//        boolean b = QueueUtils.destroyPriorityQueue(queueName);
//        log.info("通道: {} , 删除: {}", queueName, b);
//
//        for (int i = 0; i < 10; i++) {
//            int randomNum = RandomUtil.randomInt(10);
//            PriorityDemo data = new PriorityDemo();
//            data.setName("data-" + i);
//            data.setOrderNum(randomNum);
//            if (QueueUtils.addPriorityQueueObject(queueName, data)) {
//                log.info("通道: {} , 发送数据: {}", queueName, data);
//            } else {
//                log.info("通道: {} , 发送数据: {}, 发送失败", queueName, data);
//            }
//        }
//        return R.ok("操作成功");
//    }
//
//    /**
//     * 删除队列数据
//     *
//     * @param queueName 队列名
//     * @param name      对象名
//     * @param orderNum  排序号
//     */
//    @GetMapping("/remove")
//    public R<Void> remove(String queueName, String name, Integer orderNum) {
//        PriorityDemo data = new PriorityDemo();
//        data.setName(name);
//        data.setOrderNum(orderNum);
//        if (QueueUtils.removePriorityQueueObject(queueName, data)) {
//            log.info("通道: {} , 删除数据: {}", queueName, data);
//        } else {
//            return R.fail("操作失败");
//        }
//        return R.ok("操作成功");
//    }
//
//    /**
//     * 获取队列数据
//     *
//     * @param queueName 队列名
//     */
//    @GetMapping("/get")
//    public R<Void> get(String queueName) {
//        PriorityDemo data;
//        do {
//            data = QueueUtils.getPriorityQueueObject(queueName);
//            log.info("通道: {} , 获取数据: {}", queueName, data);
//        } while (data != null);
//        return R.ok("操作成功");
//    }
//
//}
ruoyi-modules/ruoyi-system/pom.xml
@@ -96,6 +96,21 @@
            <version>3.1</version>
        </dependency>
<!--        ftp服务-->
        <dependency>
            <groupId>commons-net</groupId>
            <artifactId>commons-net</artifactId>
            <version>3.6</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
        </dependency>
        <!--        <dependency>-->
<!--            <groupId>org.springframework</groupId>-->
<!--            <artifactId>spring-test</artifactId>-->
<!--        </dependency>-->
    </dependencies>
</project>
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SynchronizationController.java
@@ -2,6 +2,7 @@
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaIgnore;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
@@ -9,23 +10,38 @@
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.utils.DateUtils;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.oss.core.OssClient;
import org.dromara.common.oss.entity.SynchronousRequest;
import org.dromara.common.oss.entity.SynchronousRequest2;
import org.dromara.common.oss.entity.UploadResult;
import org.dromara.common.oss.entity.VideoRequest;
import org.dromara.common.redis.utils.QueueUtils;
import org.dromara.common.oss.factory.OssFactory;
import org.dromara.system.domain.SysOss;
import org.dromara.system.domain.properties.Boundary;
import org.dromara.system.domain.properties.FtpConfig;
import org.dromara.system.domain.vo.SysOssUploadVo;
import org.dromara.system.domain.vo.SysOssVo;
import org.dromara.system.mapper.SysOssMapper;
import org.dromara.system.service.ISysOssService;
import org.dromara.system.uitil.FtpApche;
import org.dromara.system.uitil.HttpUtils;
import org.dromara.system.uitil.PasswordUtil;
import org.jetbrains.annotations.NotNull;
import org.springframework.http.MediaType;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.io.InputStream;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.TimeUnit;
@@ -38,25 +54,23 @@
@RequestMapping("/resource/synchronization")
public class SynchronizationController {
    private final FtpConfig ftpConfig;
    private final ISysOssService ossService;
    private final SysOssMapper baseMapper;
    /**
     * 上传同步  视频专网
     *
     * @param
     * @return
     */
    @PostMapping(value = "/upload")
    public R synchronization(SynchronousRequest request) throws ParseException {
    public R synchronization(SynchronousRequest request) throws IOException, ParseException {
        if (ObjectUtil.isNull(request.getFile())) {
            return R.fail("上传文件不能为空");
        }
        log.info("ossId:{},path:{}",request.getOssId(),request.getPath());
        SysOssVo oss = ossService.upload2(request);
        //添加队列数据
        QueueUtils.addDelayedQueueObject("OSS", oss.getOssId().toString(), 1, TimeUnit.HOURS);
        log.info("通道: {} , 发送数据: {}", "OSS", oss.getOssId());
        ossService.upload2(request);
        return R.ok();
    }
@@ -66,9 +80,8 @@
            return R.fail("上传文件不能为空");
        }
        SysOssVo oss = ossService.upload3(request1);
        //添加队列数据
        QueueUtils.addDelayedQueueObject("OSS", oss.getOssId().toString(), 1, TimeUnit.DAYS);
//        QueueUtils.addDelayedQueueObject("OSS", oss.getOssId().toString(), 1, TimeUnit.DAYS);
        log.info("通道: {} , 发送数据: {}", "OSS", oss.getOssId());
        SysOssUploadVo uploadVo = new SysOssUploadVo();
        uploadVo.setUrl(oss.getUrl());
@@ -85,8 +98,74 @@
     */
    @PostMapping(value = "/delete")
    public R<Void> remove(String ossId) throws Exception {
        return ossService.deleteWithValidByIds(List.of(convert(ossId)), true,"000000") ? R.ok() : R.fail("提取码错误!");
        return ossService.deleteWithValidByIds(List.of(convert(ossId)), true, "000000") ? R.ok() : R.fail("提取码错误!");
    }
    /**
     * 上传同步  公安内网
     *
     * @param request
     */
    @PostMapping(value = "/uploadByFileName")
    public R<Void> uploadByFileName(VideoRequest request) throws Exception {
        if (ObjectUtil.isNull(request.getFile())) {
            return R.fail("上传文件不能为空");
        }
        SysOssVo oss = ossService.upload3(request);
        InputStream input = request.getFile().getInputStream();
        String fileName = request.getFileName();
        OssClient storage = OssFactory.instance();
        UploadResult uploadResult = storage.upload(input,getPath(fileName),
            fileName.substring(14,fileName.length()));
        buildResultEntity1(fileName, fileName.substring(14,fileName.length()), "minio", uploadResult);
        return R.ok();
    }
    public String getPath(String suffix) {
        return DateUtils.datePath() + "/" + suffix;
    }
    @NotNull
    private void buildResultEntity1(String originalfileName, String suffix, String configKey, UploadResult uploadResult) {
        SysOss oss = new SysOss();
        oss.setUrl(uploadResult.getUrl());
        oss.setFileSuffix(suffix);
        oss.setFileName(uploadResult.getFilename());
        oss.setOriginalName(originalfileName);
        oss.setService(configKey);
        oss.setPassword(PasswordUtil.randomPassword(6));
        baseMapper.insert(oss);
    }
        @NotNull
    private void buildResultEntity(String originalfileName, String suffix, String configKey, UploadResult uploadResult
    ,MultipartFile file) {
        SysOss oss = new SysOss();
        oss.setUrl(uploadResult.getUrl());
        oss.setFileSuffix(suffix);
        oss.setFileName(uploadResult.getFilename());
        oss.setOriginalName(originalfileName);
        oss.setService(configKey);
        oss.setPassword(PasswordUtil.randomPassword(6));
        baseMapper.insert(oss);
        //拼接同步信息
//        SynchronousRequest request = new SynchronousRequest();
//        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//        request.setFile(file);
//        request.setFileName(oss.getOriginalName()+"1");
//        request.setPath(uploadResult.getFilename());
//        request.setCreateTime(format.format(oss.getCreateTime()));
//        request.setPassword(oss.getPassword());
//        request.setCreateBy("1731588854831022081");
//        request.setOssId(String.valueOf(oss.getOssId()));
//        request.setOssId("123890");
//        post(request);
    }
    public static Long[] convert(String input) {
        String[] stringArray = input.split(",");
@@ -99,4 +178,16 @@
        return longArray;
    }
    @NotNull
    private void buildResultEntity2(String originalfileName, String suffix, String configKey, UploadResult uploadResult
        , String password) throws IOException {
        SysOss oss = new SysOss();
        oss.setUrl(uploadResult.getUrl());
        oss.setFileSuffix(suffix);
        oss.setFileName(uploadResult.getFilename());
        oss.setOriginalName(originalfileName);
        oss.setService(configKey);
        oss.setPassword(password);
        baseMapper.insert(oss);
    }
}
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysOssController.java
@@ -5,16 +5,19 @@
import cn.dev33.satoken.annotation.SaIgnore;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONArray;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.constant.CacheNames;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.core.utils.file.FileUtils;
import org.dromara.common.core.validate.QueryGroup;
import org.dromara.common.oss.entity.SynchronousRequest;
import org.dromara.common.oss.entity.VideoRequest;
import org.dromara.common.redis.utils.CacheUtils;
import org.dromara.common.redis.utils.QueueUtils;
import org.dromara.common.web.core.BaseController;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
@@ -23,6 +26,7 @@
import org.dromara.system.domain.SysOss;
import org.dromara.system.domain.bo.SysOssBo;
import org.dromara.system.domain.properties.Boundary;
import org.dromara.system.domain.properties.FtpConfig;
import org.dromara.system.domain.vo.SysOssUploadVo;
import org.dromara.system.domain.vo.SysOssVo;
import org.dromara.system.mapper.SysOssMapper;
@@ -30,7 +34,10 @@
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotEmpty;
import lombok.RequiredArgsConstructor;
import org.dromara.system.uitil.FTPUtil;
import org.dromara.system.uitil.FtpApche;
import org.dromara.system.uitil.HttpUtils;
import org.dromara.system.uitil.PasswordUtil;
import org.springframework.http.MediaType;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
@@ -55,9 +62,11 @@
@RequestMapping("/resource/oss")
public class SysOssController extends BaseController {
    private final FtpConfig ftpConfig;
    private final Boundary boundary;
    private final ISysOssService ossService;
    private final SysOssMapper ossMapper;
    /**
     * 查询OSS对象存储列表
     */
@@ -89,23 +98,18 @@
    @Log(title = "OSS对象存储", businessType = BusinessType.INSERT)
    @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public R<SysOssUploadVo> upload(@RequestPart("file") MultipartFile file) throws IOException {
        if (ObjectUtil.isNull(file)) {
            return R.fail("上传文件不能为空");
        }
        SysOssVo oss = ossService.upload(file);
        SynchronousRequest request = new SynchronousRequest();
        request.setFile(file);
        //添加队列数据
        QueueUtils.addDelayedQueueObject("OSS", oss.getOssId().toString(), 1, TimeUnit.DAYS);
        log.info("通道: {} , 发送数据: {}", "OSS", oss.getOssId());
        SysOssUploadVo uploadVo = new SysOssUploadVo();
        uploadVo.setUrl(oss.getUrl());
        uploadVo.setFileName(oss.getOriginalName());
        uploadVo.setOssId(oss.getOssId().toString());
        uploadVo.setPassword(oss.getPassword());
        return R.ok(uploadVo);
    }
@@ -118,20 +122,17 @@
//    @Log(title = "OSS对象存储", businessType = BusinessType.INSERT)
//    @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
//    public R<String> upload(@RequestPart("file") MultipartFile file) throws IOException {
//        if (ObjectUtil.isNull(file)) {
//            return R.fail("上传文件不能为空");
//        }
//        VideoRequest request = new VideoRequest();
//        request.setFile(file);
//        request.setFileName(file.getOriginalFilename());
//        String post = post(request);
//        String password = post.substring(post.indexOf("password\":\"") + 9, post.indexOf("\"}"));
//        String originalfileName = file.getOriginalFilename();
//        long l = System.currentTimeMillis();
//        String password = PasswordUtil.randomPassword(6);
//        String suffix = StringUtils.substring(originalfileName, originalfileName.lastIndexOf("."), originalfileName.length());
//        FtpApche.uploadFile(ftpConfig, file, password + String.valueOf(l) + suffix);
//        return R.ok(password);
//    }
    private String post(VideoRequest request) throws IOException {
        HashMap<String, String> headers = new HashMap<>(3);
        String requestUrl = boundary.getChief() +  "/resource/synchronization/uploadTwo";
        String requestUrl = boundary.getChief() + "/resource/synchronization/uploadTwo";
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
        headers.put("content-type", "application/json");
@@ -150,11 +151,23 @@
    @SaCheckPermission("system:oss:download")
    @GetMapping("/download/{ossId}/{password}")
    public R<String> download(@PathVariable Long ossId, @PathVariable String password, HttpServletResponse response) throws IOException {
        SysOss oss = ossMapper.selectById(ossId);
        if(!oss.getPassword().equals(password)){
            return R.fail("提取码错误!");
        if (String.valueOf(ossId).equals("12345678")) {
            SysOssVo sysOssVo = ossMapper.selectVoOne(new LambdaQueryWrapper<SysOss>()
                .eq(SysOss::getPassword, password));
            if (sysOssVo == null) {
                return R.fail("提取码错误!");
            }
            ossService.download(sysOssVo.getOssId(), response);
        } else {
            SysOss oss = ossMapper.selectById(ossId);
            if (!oss.getPassword().equals(password)) {
                return R.fail("提取码错误!");
            }
            ossService.download(ossId, response);
        }
        ossService.download(ossId, response);
        return R.ok();
    }
@@ -169,7 +182,7 @@
    public R<Void> remove(@NotEmpty(message = "主键不能为空")
                          @PathVariable Long[] ossIds,
                          @PathVariable String password) throws Exception {
        return ossService.deleteWithValidByIds(List.of(ossIds), true,password) ? R.ok() : R.fail("提取码错误!");
        return ossService.deleteWithValidByIds(List.of(ossIds), true, password) ? R.ok() : R.fail("提取码错误!");
    }
    /**
@@ -183,22 +196,22 @@
        return ossService.viewPasswordById(ossId);
    }
    /**
     * 订阅队列(监听)
     *
     * @param queueName 队列名
     */
    @GetMapping("/subscribe")
    public R<Void> subscribe(String queueName) {
        log.info("通道: {} 监听中......", queueName);
        // 项目初始化设置一次即可
        QueueUtils.subscribeBlockingQueue(queueName, (String ossId) -> {
            // 观察接收时间
            log.info("通道: {}, 收到数据: {}", queueName, ossId);
//            ossMapper.deleteById(Long.valueOf(ossId));
        });
        return R.ok("操作成功");
    }
//    /**
//     * 订阅队列(监听)
//     *
//     * @param queueName 队列名
//     */
//    @GetMapping("/subscribe")
//    public R<Void> subscribe(String queueName) {
//        log.info("通道: {} 监听中......", queueName);
//        // 项目初始化设置一次即可
//        QueueUtils.subscribeBlockingQueue(queueName, (String ossId) -> {
//            // 观察接收时间
//            log.info("通道: {}, 收到数据: {}", queueName, ossId);
////            ossMapper.deleteById(Long.valueOf(ossId));
//        });
//        return R.ok("操作成功");
//    }
}
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/properties/FtpConfig.java
New file
@@ -0,0 +1,24 @@
package org.dromara.system.domain.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
 * ftp实体类
 */
@Data
@Component
@ConfigurationProperties(prefix = "ftp")
public class FtpConfig {
    private String url;
    private String port;
    private String username;
    private String passwd;
    private String path;
}
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/runner/SystemApplicationRunner.java
@@ -1,14 +1,15 @@
package org.dromara.system.runner;
import cn.hutool.extra.ftp.Ftp;
import com.amazonaws.services.s3.AmazonS3;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.oss.core.OssClient;
import org.dromara.common.oss.factory.OssFactory;
import org.dromara.common.oss.properties.OssProperties;
import org.dromara.common.redis.utils.QueueUtils;
import org.dromara.system.convert.SysOssConfigClassConvert;
import org.dromara.system.domain.SysOssConfig;
import org.dromara.system.domain.properties.Boundary;
import org.dromara.system.domain.properties.FtpConfig;
import org.dromara.system.domain.vo.SysOssVo;
import org.dromara.system.mapper.SysOssConfigMapper;
import org.dromara.system.mapper.SysOssMapper;
@@ -16,6 +17,7 @@
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.system.service.ISysOssService;
import org.dromara.system.uitil.FtpApche;
import org.dromara.system.uitil.HttpUtils;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
@@ -35,35 +37,44 @@
@Component
public class SystemApplicationRunner implements ApplicationRunner {
    private final Boundary boundary;
//    private final FtpConfig ftpConfig;
//    private final Boundary boundary;
    private final ISysOssConfigService ossConfigService;
    private final SysOssMapper ossMapper;
    private final SysOssConfigMapper sysOssConfigMapper;
//    private final SysOssMapper ossMapper;
//    private final SysOssConfigMapper sysOssConfigMapper;
//    private final AmazonS3 client;
    @Override
    public void run(ApplicationArguments args) throws Exception {
//        FtpApche.connect(ftpConfig);
//        log.info("连接ftp服务成功");
        ossConfigService.init();
        //初始化ossclient
        List<SysOssConfig> collect = sysOssConfigMapper.selectList().stream().filter(e -> e.getStatus().equals("0"))
            .collect(Collectors.toList());
        OssProperties properties = SysOssConfigClassConvert.INSTANCE.sysOssConfigToossProperties(collect.get(0));
        OssClient client = new OssClient("minio",properties);
        log.info("初始化OSS配置成功");
        log.info("通道: {} 监听中......", "OSS");
//        List<SysOssConfig> collect = sysOssConfigMapper.selectList().stream().filter(e -> e.getStatus().equals("0"))
//            .collect(Collectors.toList());
//        OssProperties properties = SysOssConfigClassConvert.INSTANCE.sysOssConfigToossProperties(collect.get(0));
//        OssClient client = new OssClient("minio",properties);
//        log.info("初始化OSS配置成功");
//        log.info("通道: {} 监听中......", "OSS");
        // 项目初始化设置一次即可
        QueueUtils.subscribeBlockingQueue("OSS", (String ossId) -> {
            // 观察接收时间
            log.info("通道: {}, 收到数据: {}", "OSS", ossId);
            SysOssVo vo = ossMapper.selectVoById(ossId);
            client.delete( vo.getUrl());
            ossMapper.deleteById(Long.valueOf(ossId));
            try {
                HttpUtils.sendDeleteRequest(boundary.getVideo() + "/resource/synchronization/delete",ossId);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        });
//        QueueUtils.subscribeBlockingQueue("OSS", (String name) -> {
//            // 观察接收时间
//            log.info("通道: {}, 收到数据: {}", "OSS", name);
////            try {
////                HttpUtils.sendDeleteRequest2(boundary.getLocalhost() + "/resource/synchronization/uploadByFileName",name);
////            } catch (IOException e) {
////                throw new RuntimeException(e);
////            }
//
////            SysOssVo vo = ossMapper.selectVoById(ossId);
////            client.delete( vo.getUrl());
////            ossMapper.deleteById(Long.valueOf(ossId));
////            try {
////                HttpUtils.sendDeleteRequest(boundary.getVideo() + "/resource/synchronization/delete",ossId);
////            } catch (IOException e) {
////                throw new RuntimeException(e);
////            }
//        });
    }
}
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysOssService.java
@@ -35,6 +35,8 @@
    SysOssVo upload2(SynchronousRequest request) throws ParseException;
    SysOssVo upload3(SynchronousRequest request) throws ParseException;
//    SysOssVo upload(File file);
    void download(Long ossId, HttpServletResponse response) throws IOException;
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssServiceImpl.java
@@ -10,6 +10,8 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.github.therapi.runtimejavadoc.repack.com.eclipsesource.json.Json;
import org.apache.commons.io.IOUtils;
import org.apache.xmlbeans.impl.common.IOUtil;
import org.dromara.common.core.constant.CacheNames;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.domain.model.LoginUser;
@@ -45,9 +47,7 @@
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.*;
import java.net.URLEncoder;
import java.text.ParseException;
import java.text.SimpleDateFormat;
@@ -177,13 +177,39 @@
    }
    /**
     * 同步
     * 同步 定时拉取
     * @param
     * @param
     * @return
     */
    @Override
    public SysOssVo upload2(SynchronousRequest request) throws ParseException {
        String originalfileName = request.getFileName();
        String suffix = StringUtils.substring(originalfileName, originalfileName.lastIndexOf("."), originalfileName.length());
        OssClient storage = OssFactory.instance();
//        String type = request.getFileName().substring(14,request.getFileName().length());
        String type = request.getFile().getContentType();
        UploadResult uploadResult;
        try {
            uploadResult = storage.uploadSuffix2(request.getFile().getBytes(), request.getPath(), type);
        } catch (IOException e) {
            throw new ServiceException(e.getMessage());
        }
        // 保存文件信息
        System.out.println("canshu:" + "," + originalfileName +"," + suffix +"," + storage.getConfigKey() +"," + uploadResult + "," +request.getOssId()
         +"," + request.getPassword() +"," + request.getCreateBy() +"," + request.getFileName() +"," +  request.getCreateTime());
        return buildResultEntity2(originalfileName, suffix, storage.getConfigKey(), uploadResult,request.getOssId(),
            request.getPassword(), "1731588854831022081", request.getFileName(), request.getCreateTime());
    }
    /**
     * 同步 定时拉取
     * @param
     * @param
     * @return
     */
    @Override
    public SysOssVo upload3(SynchronousRequest request) throws ParseException {
        String originalfileName = request.getFileName();
        String suffix = StringUtils.substring(originalfileName, originalfileName.lastIndexOf("."), originalfileName.length());
        OssClient storage = OssFactory.instance();
@@ -195,7 +221,7 @@
        }
        // 保存文件信息
        System.out.println("canshu:" + "," + originalfileName +"," + suffix +"," + storage.getConfigKey() +"," + uploadResult + "," +request.getOssId()
         +"," + request.getPassword() +"," + request.getCreateBy() +"," + request.getFileName() +"," +  request.getCreateTime());
            +"," + request.getPassword() +"," + request.getCreateBy() +"," + request.getFileName() +"," +  request.getCreateTime());
        return buildResultEntity2(originalfileName, suffix, storage.getConfigKey(), uploadResult,request.getOssId(),
            request.getPassword(), "1731588854831022081", request.getFileName(), request.getCreateTime());
    }
@@ -230,8 +256,8 @@
        request.setPassword(oss.getPassword());
        request.setCreateBy("1731588854831022081");
        request.setOssId(String.valueOf(oss.getOssId()));
//        request.setOssId();
//        post(request);
//        request.setOssId("123890");
        post(request);
        SysOssVo sysOssVo = MapstructUtils.convert(oss, SysOssVo.class);
        return this.matchingUrl(sysOssVo);
@@ -281,13 +307,15 @@
            storage.delete(sysOss.getUrl());
            if(sysOss.getPassword().equals(password) || password.equals("000000")){
                baseMapper.deleteById(sysOss.getOssId());
                HttpUtils.sendDeleteRequest(boundary.getVideo() + "/resource/synchronization/delete",String.valueOf(sysOss.getOssId()));
//                HttpUtils.sendDeleteRequest(boundary.getVideo() + "/resource/synchronization/delete",String.valueOf(sysOss.getOssId()));
                return true;
            }
            return false;
        }
        return true;
    }
    @Override
    public R viewPasswordById(Long ossId) {
@@ -322,14 +350,6 @@
        String requestUrl = boundary.getVideo() +  "/resource/synchronization/upload";
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
        String jsonString = "";
        try {
            // 将Java对象转换为JSON字符串
            jsonString = objectMapper.writeValueAsString(request);
//            System.out.println(jsonString);
        } catch (Exception e) {
//            e.printStackTrace();
        }
        headers.put("content-type", "application/json");
        // 发送post请求
        String resultData = HttpUtils.sendPostRequest(requestUrl, request);
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/sync/VideoPulSync.java
New file
@@ -0,0 +1,117 @@
package org.dromara.system.sync;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import org.dromara.common.core.utils.DateUtils;
import org.dromara.common.oss.core.OssClient;
import org.dromara.common.oss.entity.SynchronousRequest;
import org.dromara.common.oss.entity.SynchronousRequest2;
import org.dromara.common.oss.entity.UploadResult;
import org.dromara.common.oss.entity.VideoRequest;
import org.dromara.common.oss.factory.OssFactory;
import org.dromara.system.domain.SysOss;
import org.dromara.system.domain.properties.Boundary;
import org.dromara.system.domain.properties.FtpConfig;
import org.dromara.system.mapper.SysOssMapper;
import org.dromara.system.uitil.FtpApche;
import org.dromara.system.uitil.HttpUtils;
import org.dromara.system.uitil.PasswordUtil;
import org.jetbrains.annotations.NotNull;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@Component
public class VideoPulSync {
    private final Boundary boundary;
    private final FtpConfig ftpConfig;
    private final SysOssMapper baseMapper;
    public VideoPulSync(Boundary boundary, FtpConfig ftpConfig, SysOssMapper baseMapper) {
        this.boundary = boundary;
        this.ftpConfig = ftpConfig;
        this.baseMapper = baseMapper;
    }
//    @Scheduled(cron = "0 0/1 * * * ?")
    public void get() throws IOException {
        System.out.println("同步上传......");
        List<String> list = FtpApche.downloadList(ftpConfig);
        if(list.size() == 0){
            return;
        }
        for (String str : list) {
            //密码
            String password = str.substring(0,6);
            //文件名称
            String fileName = str.substring(6,str.length());
            InputStream input = FtpApche.downloadFileFromDailyDir(str);
            byte[] fileBytesByName = FtpApche.getFileBytesByName(input);
            OssClient storage = OssFactory.instance();
            UploadResult uploadResult = storage.upload(input,getPath(fileName),fileName.substring(14,fileName.length()));
            FtpApche.deleteFile(str);
            MultipartFile file = new MockMultipartFile(fileName,fileName, fileName.substring(14,fileName.length()), input);
            buildResultEntity(fileName, fileName.substring(14,fileName.length()), "minio", uploadResult, file,
                password, fileBytesByName);
        }
    }
    @NotNull
    private void buildResultEntity(String originalfileName, String suffix, String configKey, UploadResult uploadResult
        , MultipartFile file, String password, byte[] fileBytesByName) throws IOException {
        SysOss oss = new SysOss();
        oss.setUrl(uploadResult.getUrl());
        oss.setFileSuffix(suffix);
        oss.setFileName(uploadResult.getFilename());
        oss.setOriginalName(originalfileName);
        oss.setService(configKey);
        oss.setPassword(password);
        baseMapper.insert(oss);
        //拼接同步信息
        SynchronousRequest request = new SynchronousRequest();
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        request.setFile(file);
        request.setFileName(oss.getOriginalName());
        request.setPath(uploadResult.getFilename());
        request.setCreateTime(format.format(oss.getCreateTime()));
        request.setPassword(oss.getPassword());
        request.setCreateBy("1731588854831022081");
        request.setOssId(String.valueOf(oss.getOssId()));
//        request.setOssId("999999900");
        post(request, fileBytesByName);
    }
    private String post(SynchronousRequest request, byte[] bytes) throws IOException {
        HashMap<String, String> headers = new HashMap<>(3);
        String requestUrl = boundary.getVideo() +  "/resource/synchronization/upload";
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
        headers.put("content-type", "application/json");
        // 发送post请求
        String resultData = HttpUtils.sendPostRequest2(requestUrl, request, bytes);
        // 并接收返回结果
        System.out.println(resultData);
        return resultData;
    }
    public String getPath(String suffix) {
        return DateUtils.datePath() + "/" + suffix;
    }
}
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/sync/deleteSync.java
New file
@@ -0,0 +1,43 @@
package org.dromara.system.sync;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.dromara.system.domain.SysOss;
import org.dromara.system.domain.properties.Boundary;
import org.dromara.system.domain.vo.SysOssVo;
import org.dromara.system.mapper.SysOssMapper;
import org.dromara.system.uitil.HttpUtils;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.List;
@Component
public class deleteSync {
    private final SysOssMapper baseMapper;
    private final Boundary boundary;
    public deleteSync(SysOssMapper baseMapper, Boundary boundary) {
        this.baseMapper = baseMapper;
        this.boundary = boundary;
    }
//    @Scheduled(cron = "0 */10 * * * ?")
    public void remove() throws IOException {
        System.out.println("同步删除......");
        List<SysOssVo> list = baseMapper.selectVoList(new LambdaQueryWrapper<SysOss>()
            .le(SysOss::getCreateTime, LocalDateTime.now().minusDays(1)));
        if (list.size() == 0) {
            return;
        }
        for (SysOssVo vo : list) {
            baseMapper.deleteById(vo.getOssId());
            HttpUtils.sendDeleteRequest(boundary.getVideo() + "/resource/synchronization/delete",
                String.valueOf(vo.getOssId()));
        }
    }
}
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/uitil/FTPUtil.java
New file
@@ -0,0 +1,547 @@
package org.dromara.system.uitil;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import org.dromara.system.domain.properties.FtpConfig;
import java.io.*;
import java.net.UnknownHostException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
/**
 * FTP工具类
 *
 * @author: simon
 * @date: 2019/7/3 14:37
 */
public class FTPUtil {
    /**
     * 字符集
     */
    private static final String DEFAULT_CHARSET = "UTF-8";
    /**
     * 超时时间
     */
    private static final int DEFAULT_TIMEOUT = 60 * 1000;
    /**
     * 主机名或者ip地址
     */
    private final String host;
    /**
     * ftp服务器端口
     */
    private final int port;
    /**
     * ftp用户名
     */
    private final String username;
    /**
     * ftp密码
     */
    private final String password;
    /**
     * ftpClient对象
     */
    private FTPClient ftpClient;
    /**
     * 初始化时ftp服务器路径
     */
    private volatile String ftpBasePath = "";
    /**
     * 构造函数
     *
     * @param host     主机名或者ip地址
     * @param username ftp 用户名
     * @param password ftp 密码
     * @param ftpBasePath 初始化时ftp服务器路径
     */
    private FTPUtil(String host, String username, String password,String ftpBasePath) {
        this(host, 21, username, password, DEFAULT_CHARSET,ftpBasePath);
        setTimeout(DEFAULT_TIMEOUT, DEFAULT_TIMEOUT, DEFAULT_TIMEOUT);
    }
    /**
     * 构造函数
     *
//     * @param host     主机名或者ip地址
//     * @param username ftp 用户名
//     * @param password ftp 密码
//     * @param ftpBasePath 初始化时ftp服务器路径
     */
    public FTPUtil(FtpConfig ftpConfig) {
        this(ftpConfig.getUrl(), 21, ftpConfig.getUsername(), ftpConfig.getPasswd(), DEFAULT_CHARSET,ftpConfig.getPath());
        setTimeout(DEFAULT_TIMEOUT, DEFAULT_TIMEOUT, DEFAULT_TIMEOUT);
    }
    /**
     * 构造函数
     *
     * @param host     主机名或者ip地址
     * @param port     ftp 端口
     * @param username 用户名
     * @param password 密码
     * @param ftpBasePath 初始化时ftp服务器路径
     */
    private FTPUtil(String host, int port, String username, String password, String charset, String ftpBasePath) {
        ftpClient = new FTPClient();
        ftpClient.setControlEncoding(charset);
        this.host = StringUtils.isEmpty(host) ? "localhost" : host;
        this.port = (port <= 0) ? 21 : port;
        this.username = StringUtils.isEmpty(username) ? "anonymous" : username;
        this.password = password;
        this.ftpBasePath = ftpBasePath;
    }
    /**
     * 创建默认的ftp客户端
     *
     * @param host     主机名或者ip地址
     * @param username ftp用户名
     * @param password ftp密码
     * @param ftpBasePath 初始化时ftp服务器路径
     * @return com.siniswift.efb.acars.utils.FTPUtil
     */
    public static FTPUtil createFtpCli(String host, String username, String password,String ftpBasePath) {
        return new FTPUtil(host, username, password,ftpBasePath);
    }
    /**
     * 创建自定义属性的ftp客户端
     *
     * @param host     主机名或者ip地址
     * @param port     ftp端口
     * @param username ftp用户名
     * @param password ftp密码
     * @param charset  字符集
     * @param ftpBasePath 初始化时ftp服务器路径
     * @return com.siniswift.efb.acars.utils.FTPUtil
     */
    public static FTPUtil createFtpCli(String host, int port, String username, String password, String charset,String ftpBasePath) {
        return new FTPUtil(host, port, username, password, charset,ftpBasePath);
    }
    /**
     * 设置超时时间
     *
     * @param defaultTimeout 超时时间
     * @param connectTimeout 超时时间
     * @param dataTimeout    超时时间
     */
    private void setTimeout(int defaultTimeout, int connectTimeout, int dataTimeout) {
        ftpClient.setDefaultTimeout(defaultTimeout);
        ftpClient.setConnectTimeout(connectTimeout);
        ftpClient.setDataTimeout(dataTimeout);
    }
    /**
     * 连接到ftp
     */
    public void connect() throws IOException {
        try {
            ftpClient.connect(host, port);
        } catch (UnknownHostException e) {
            throw new IOException("Can't find FTP server :" + host);
        }
        int reply = ftpClient.getReplyCode();
        if (!FTPReply.isPositiveCompletion(reply)) {
            disconnect();
            throw new IOException("Can't connect to server :" + host);
        }
        if (!ftpClient.login(username, password)) {
            disconnect();
            throw new IOException("Can't login to server :" + host);
        }
        // set data transfer mode.
        ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
        // Use passive mode to pass firewalls.
        ftpClient.enterLocalPassiveMode();
        initFtpBasePath();
    }
    /**
     * 连接ftp时保存刚登陆ftp时的路径
     */
    private void initFtpBasePath() throws IOException {
        if (StringUtils.isEmpty(ftpBasePath)) {
            synchronized (this) {
                if (StringUtils.isEmpty(ftpBasePath)) {
                    ftpBasePath = ftpClient.printWorkingDirectory();
                }
            }
        }
    }
    /**
     * ftp是否处于连接状态,是连接状态返回<tt>true</tt>
     *
     * @return boolean  是连接状态返回<tt>true</tt>
     */
    public boolean isConnected() {
        return ftpClient.isConnected();
    }
    /**
     * 上传文件到对应目录下
     *
     * @param fileName    文件名
     * @param inputStream 文件输入流
     * @param uploadDir   上传文件的父路径
     * @return java.lang.String
     */
    public String uploadFile(String fileName, InputStream inputStream, String uploadDir) throws IOException {
        changeWorkingDirectory(ftpBasePath);
        SimpleDateFormat dateFormat = new SimpleDateFormat("/yyyy/MM/dd");
        makeDirs(uploadDir);
        storeFile(fileName, inputStream);
        return uploadDir + "/" + fileName;
    }
    /**
     * 根据uploadFile返回的路径,从ftp下载文件到指定输出流中
     *
     * @param ftpRealFilePath 方法uploadFile返回的路径
     * @param outputStream    输出流
     */
    public void downloadFileFromDailyDir(String ftpRealFilePath, OutputStream outputStream) throws IOException {
        changeWorkingDirectory(ftpBasePath);
        ftpClient.retrieveFile(ftpRealFilePath, outputStream);
    }
    /**
     * 获取ftp上指定文件名到输出流中
     *
     * @param ftpFileName 文件在ftp上的路径  如绝对路径 /home/ftpuser/123.txt 或者相对路径 123.txt
     * @param out         输出流
     */
    public void retrieveFile(String ftpFileName, OutputStream out) throws IOException {
        try {
            FTPFile[] fileInfoArray = ftpClient.listFiles(ftpFileName);
            if (fileInfoArray == null || fileInfoArray.length == 0) {
                throw new FileNotFoundException("File '" + ftpFileName + "' was not found on FTP server.");
            }
            FTPFile fileInfo = fileInfoArray[0];
            if (fileInfo.getSize() > Integer.MAX_VALUE) {
                throw new IOException("File '" + ftpFileName + "' is too large.");
            }
            if (!ftpClient.retrieveFile(ftpFileName, out)) {
                throw new IOException("Error loading file '" + ftpFileName + "' from FTP server. Check FTP permissions and path.");
            }
            out.flush();
        } finally {
            closeStream(out);
        }
    }
    /**
     * 将输入流存储到指定的ftp路径下
     *
     * @param ftpFileName 文件在ftp上的路径 如绝对路径 /home/ftpuser/123.txt 或者相对路径 123.txt
     * @param in          输入流
     */
    private void storeFile(String ftpFileName, InputStream in) throws IOException {
        try {
            if (!ftpClient.storeFile(ftpFileName, in)) {
                throw new IOException("Can't upload file '" + ftpFileName + "' to FTP server. Check FTP permissions and path.");
            }
        } finally {
            closeStream(in);
        }
    }
    /**
     * 根据文件ftp路径名称删除文件
     *
     * @param ftpFileName 文件ftp路径名称
     */
    public void deleteFile(String ftpFileName) throws IOException {
        if (!ftpClient.deleteFile(ftpFileName)) {
            throw new IOException("Can't remove file '" + ftpFileName + "' from FTP server.");
        }
    }
    /**
     * 上传文件到ftp
     *
     * @param ftpFileName 上传到ftp文件路径名称
     * @param localFile   本地文件路径名称
     */
    public void upload(String ftpFileName, File localFile) throws IOException {
        if (!localFile.exists()) {
            throw new IOException("Can't upload '" + localFile.getAbsolutePath() + "'. This file doesn't exist.");
        }
        InputStream in = null;
        try {
            in = new BufferedInputStream(new FileInputStream(localFile));
            if (!ftpClient.storeFile(ftpFileName, in)) {
                throw new IOException("Can't upload file '" + ftpFileName + "' to FTP server. Check FTP permissions and path.");
            }
        } finally {
            closeStream(in);
        }
    }
    /**
     * 上传文件夹到ftp上
     *
     * @param remotePath ftp上文件夹路径名称
     * @param localPath  本地上传的文件夹路径名称
     */
    public void uploadDir(String remotePath, String localPath) throws IOException {
        localPath = localPath.replace("\\\\", "/");
        File file = new File(localPath);
        if (file.exists()) {
            if (!ftpClient.changeWorkingDirectory(remotePath)) {
                ftpClient.makeDirectory(remotePath);
                ftpClient.changeWorkingDirectory(remotePath);
            }
            File[] files = file.listFiles();
            if (null != files) {
                for (File f : files) {
                    if (f.isDirectory() && !f.getName().equals(".") && !f.getName().equals("..")) {
                        uploadDir(remotePath + "/" + f.getName(), f.getPath());
                    } else if (f.isFile()) {
                        upload(remotePath + "/" + f.getName(), f);
                    }
                }
            }
        }
    }
    /**
     * 下载ftp文件到本地上
     *
     * @param ftpFileName ftp文件路径名称
     * @param localFile   本地文件路径名称
     */
    public void download(String ftpFileName, File localFile) throws IOException {
        OutputStream out = null;
        try {
            FTPFile[] fileInfoArray = ftpClient.listFiles(ftpFileName);
            if (fileInfoArray == null || fileInfoArray.length == 0) {
                throw new FileNotFoundException("File " + ftpFileName + " was not found on FTP server.");
            }
            FTPFile fileInfo = fileInfoArray[0];
            if (fileInfo.getSize() > Integer.MAX_VALUE) {
                throw new IOException("File " + ftpFileName + " is too large.");
            }
            out = new BufferedOutputStream(new FileOutputStream(localFile));
            if (!ftpClient.retrieveFile(ftpFileName, out)) {
                throw new IOException("Error loading file " + ftpFileName + " from FTP server. Check FTP permissions and path.");
            }
            out.flush();
        } finally {
            closeStream(out);
        }
    }
    /**
     * 改变工作目录
     *
     * @param dir ftp服务器上目录
     * @return boolean 改变成功返回true
     */
    public boolean changeWorkingDirectory(String dir) {
        if (!ftpClient.isConnected()) {
            return false;
        }
        try {
            return ftpClient.changeWorkingDirectory(dir);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }
    /**
     * 下载ftp服务器下文件夹到本地
     *
     * @param remotePath ftp上文件夹路径名称
     * @param localPath  本地上传的文件夹路径名称
     */
    public void downloadDir(String remotePath, String localPath) throws IOException {
        localPath = localPath.replace("\\\\", "/");
        File file = new File(localPath);
        if (!file.exists()) {
            file.mkdirs();
        }
        FTPFile[] ftpFiles = ftpClient.listFiles(remotePath);
        for (int i = 0; ftpFiles != null && i < ftpFiles.length; i++) {
            FTPFile ftpFile = ftpFiles[i];
            if (ftpFile.isDirectory() && !ftpFile.getName().equals(".") && !ftpFile.getName().equals("..")) {
                downloadDir(remotePath + "/" + ftpFile.getName(), localPath + "/" + ftpFile.getName());
            } else {
                download(remotePath + "/" + ftpFile.getName(), new File(localPath + "/" + ftpFile.getName()));
            }
        }
    }
    /**
     * 列出ftp上文件目录下的文件
     *
     * @param filePath ftp上文件目录
     * @return java.util.List<java.lang.String>
     */
    public List<String> listFileNames(String filePath) throws IOException {
        FTPFile[] ftpFiles = ftpClient.listFiles(filePath);
        List<String> fileList = new ArrayList<>();
        if (ftpFiles != null) {
            for (int i = 0; i < ftpFiles.length; i++) {
                FTPFile ftpFile = ftpFiles[i];
                if (ftpFile.isFile()) {
                    fileList.add(ftpFile.getName());
                }
            }
        }
        return fileList;
    }
    /**
     * 发送ftp命令到ftp服务器中
     *
     * @param args ftp命令
     */
    public void sendSiteCommand(String args) throws IOException {
        if (!ftpClient.isConnected()) {
            ftpClient.sendSiteCommand(args);
        }
    }
    /**
     * 获取当前所处的工作目录
     *
     * @return java.lang.String 当前所处的工作目录
     */
    public String printWorkingDirectory() {
        if (!ftpClient.isConnected()) {
            return "";
        }
        try {
            return ftpClient.printWorkingDirectory();
        } catch (IOException e) {
            // do nothing
        }
        return "";
    }
    /**
     * 切换到当前工作目录的父目录下
     *
     * @return boolean 切换成功返回true
     */
    public boolean changeToParentDirectory() {
        if (!ftpClient.isConnected()) {
            return false;
        }
        try {
            return ftpClient.changeToParentDirectory();
        } catch (IOException e) {
            // do nothing
        }
        return false;
    }
    /**
     * 返回当前工作目录的上一级目录
     *
     * @return java.lang.String 当前工作目录的父目录
     */
    public String printParentDirectory() {
        if (!ftpClient.isConnected()) {
            return "";
        }
        String w = printWorkingDirectory();
        changeToParentDirectory();
        String p = printWorkingDirectory();
        changeWorkingDirectory(w);
        return p;
    }
    /**
     * 创建目录
     *
     * @param pathname 路径名
     * @return boolean 创建成功返回true
     */
    public boolean makeDirectory(String pathname) throws IOException {
        return ftpClient.makeDirectory(pathname);
    }
    /**
     * 创建多个目录
     *
     * @param pathname 路径名
     */
    public void makeDirs(String pathname) throws IOException {
        pathname = pathname.replace("\\\\", "/");
        String[] pathnameArray = pathname.split("/");
        for (String each : pathnameArray) {
            if (StringUtils.isNotEmpty(each)) {
                ftpClient.makeDirectory(each);
                ftpClient.changeWorkingDirectory(each);
            }
        }
    }
    /**
     * 关闭流
     *
     * @param stream 流
     */
    private static void closeStream(Closeable stream) {
        if (stream != null) {
            try {
                stream.close();
            } catch (IOException ex) {
                // do nothing
            }
        }
    }
    /**
     * 关闭ftp连接
     */
    public void disconnect() {
        if (null != ftpClient && ftpClient.isConnected()) {
            try {
                ftpClient.logout();
                ftpClient.disconnect();
            } catch (IOException ex) {
                // do nothing
            }
        }
    }
}
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/uitil/FtpApche.java
New file
@@ -0,0 +1,193 @@
package org.dromara.system.uitil;
import cn.hutool.extra.ftp.Ftp;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import org.dromara.system.domain.properties.FtpConfig;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
/**
 * 实现FTP文件上传和文件下载
 */
public class FtpApche {
    private static FTPClient ftpClient = new FTPClient();
    private static String encoding = System.getProperty("file.encoding");
    /**
     * 超时时间
     */
    private static final int DEFAULT_TIMEOUT = 60 * 1000;
    /**
     * 连接到ftp
     */
    public static void connect(FtpConfig config) throws IOException {
        setTimeout();
        try {
            ftpClient.connect(config.getUrl(), 21);
        } catch (UnknownHostException e) {
            throw new IOException("Can't find FTP server :" + "21");
        }
        int reply = ftpClient.getReplyCode();
        if (!FTPReply.isPositiveCompletion(reply)) {
            disconnect();
            throw new IOException("Can't connect to server :" + "21");
        }
        if (!ftpClient.login(config.getUsername(), config.getPasswd())) {
            disconnect();
            throw new IOException("Can't login to server :" + "21");
        }
        // set data transfer mode.
        ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
        // Use passive mode to pass firewalls.
        ftpClient.enterLocalPassiveMode();
    }
    /**
     * 设置超时时间
     */
    private static void setTimeout() {
        ftpClient.setDefaultTimeout(DEFAULT_TIMEOUT);
        ftpClient.setConnectTimeout(DEFAULT_TIMEOUT);
        ftpClient.setDataTimeout(DEFAULT_TIMEOUT);
    }
    /**
     * ftp是否处于连接状态,是连接状态返回<tt>true</tt>
     *
     * @return boolean  是连接状态返回<tt>true</tt>
     */
    public static boolean isConnected() {
        return ftpClient.isConnected();
    }
    /**
     * 关闭ftp连接
     */
    public static void disconnect() {
        if (null != ftpClient && ftpClient.isConnected()) {
            try {
                ftpClient.logout();
                ftpClient.disconnect();
            } catch (IOException ex) {
                // do nothing
            }
        }
    }
    /**
     * 改变工作目录
     *
     * @param dir ftp服务器上目录
     * @return boolean 改变成功返回true
     */
    public static boolean changeWorkingDirectory(String dir) {
        try {
            return ftpClient.changeWorkingDirectory(dir);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }
    /**
     * Description: 向FTP服务器上传文件
     *
     * @return 成功返回true,否则返回false
     */
    public static boolean uploadFile(FtpConfig config, MultipartFile file, String name) throws IOException {
        boolean result = false;
        InputStream input = file.getInputStream();
        try {
            connect(config);
            ftpClient.setBufferSize(1024 * 1024);
            result = ftpClient.storeFile(new String(name.getBytes(encoding), "iso-8859-1"), input);
            if (result) {
                System.out.println("上传成功!");
            }
            input.close();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            ftpClient.logout();
            ftpClient.disconnect();
        }
        return result;
    }
    /**
     * 根据文件ftp路径名称删除文件
     *
     * @param ftpFileName 文件ftp路径名称
     */
    public static void deleteFile(String ftpFileName) throws IOException {
        int dele = ftpClient.dele(ftpFileName);
        try {
            ftpClient.logout();
            ftpClient.disconnect();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    /**
     * 根据uploadFile返回的路径,从ftp下载文件到指定输出流中
     *
     * @param filename 文件名称
     */
    public static InputStream downloadFileFromDailyDir(String filename) throws IOException {
        return ftpClient.retrieveFileStream(filename);
    }
    public static List<String> downloadList(FtpConfig config) throws IOException {
        connect(config);
        List<String> list = new ArrayList<>();
        FTPFile[] ftpFiles = ftpClient.listFiles();
        if (ftpFiles.length == 0) {
            return list;
        }
        for (FTPFile ftpFile : ftpFiles) {
            list.add(ftpFile.getName());
        }
        return list;
    }
    /**
     * 根据名称获取文件,以字节数组返回
     *
     * @param ftpPath  FTP服务器文件相对路径,例如:test/123
     * @param fileName 文件名,例如:test.xls
     * @return byte[] 字节数组对象
     */
    public static byte[] getFileBytesByName(InputStream is) throws IOException {
        //创建byte数组输出流
        ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
        try {
            try {
                byte[] buffer = new byte[1024 * 1024 * 4];
                int len = -1;
                while ((len = is.read(buffer, 0, 1024 * 1024 * 4)) != -1) {
                    byteStream.write(buffer, 0, len);
                }
            } catch (Exception e) {
                System.out.println(e);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return byteStream.toByteArray();
    }
}
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/uitil/HttpUtils.java
@@ -5,6 +5,7 @@
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
@@ -14,6 +15,7 @@
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import org.dromara.common.oss.entity.SynchronousRequest;
import org.dromara.common.oss.entity.SynchronousRequest2;
import org.dromara.common.oss.entity.VideoRequest;
import org.springframework.web.multipart.MultipartFile;
@@ -25,36 +27,6 @@
import java.util.HashMap;
public class HttpUtils {
    public static String sendPostWithJson(String url, String jsonStr, HashMap<String,String> headers) {
        // 返回的结果
        String jsonResult = "";
        try {
            HttpClient client = new HttpClient();
            // 连接超时
            client.getHttpConnectionManager().getParams().setConnectionTimeout(3*1000);
            // 读取数据超时
            client.getHttpConnectionManager().getParams().setSoTimeout(3*60*1000);
            client.getParams().setContentCharset("UTF-8");
            PostMethod postMethod = new PostMethod(url);
            postMethod.setRequestHeader("content-type", headers.get("content-type"));
            // 非空
            if (null != jsonStr && !"".equals(jsonStr)) {
                StringRequestEntity requestEntity = new StringRequestEntity(jsonStr, headers.get("content-type"), "UTF-8");
                postMethod.setRequestEntity(requestEntity);
            }
            int status = client.executeMethod(postMethod);
            if (status == HttpStatus.SC_OK) {
                jsonResult = postMethod.getResponseBodyAsString();
            } else {
                throw new RuntimeException("接口连接失败!");
            }
        } catch (Exception e) {
            throw new RuntimeException("接口连接失败!");
        }
        return jsonResult;
    }
    private static final String BOUNDARY = "----WebKitFormBoundary7MA4YWxkTrZu0gW";
    private static final String LINE_FEED = "\r\n";
@@ -66,59 +38,8 @@
        return file;
    }
    public static void sendPostRequest(String url, String stringParam, File fileParam) throws Exception {
        URL obj = new URL(url);
        HttpURLConnection con = (HttpURLConnection) obj.openConnection();
        // 设置POST请求
        con.setRequestMethod("POST");
        con.setDoOutput(true);
        con.setDoInput(true);
        con.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY);
        // 构建请求体
        DataOutputStream wr = new DataOutputStream(con.getOutputStream());
        wr.writeBytes("--" + BOUNDARY + LINE_FEED);
        wr.writeBytes("Content-Disposition: form-data; name=\"stringParam\"" + LINE_FEED);
        wr.writeBytes(LINE_FEED);
        wr.writeBytes(stringParam + LINE_FEED);
        wr.writeBytes("--" + BOUNDARY + LINE_FEED);
        wr.writeBytes("Content-Disposition: form-data; name=\"fileParam\"; filename=\"" + fileParam.getName() + "\"" + LINE_FEED);
        wr.writeBytes("Content-Type: " + HttpURLConnection.guessContentTypeFromName(fileParam.getName()) + LINE_FEED);
        wr.writeBytes("Content-Transfer-Encoding: binary" + LINE_FEED);
        wr.writeBytes(LINE_FEED);
        FileInputStream inputStream = new FileInputStream(fileParam);
        byte[] buffer = new byte[4096];
        int bytesRead = -1;
        while ((bytesRead = inputStream.read(buffer)) != -1) {
            wr.write(buffer, 0, bytesRead);
        }
        wr.writeBytes(LINE_FEED);
        wr.writeBytes("--" + BOUNDARY + "--" + LINE_FEED);
        wr.flush();
        wr.close();
        // 发送请求并获取响应
        int responseCode = con.getResponseCode();
        BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
        String inputLine;
        StringBuffer response = new StringBuffer();
        while ((inputLine = in.readLine()) != null) {
            response.append(inputLine);
        }
        in.close();
        // 打印响应结果
        System.out.println("Response Code : " + responseCode);
        System.out.println("Response : " + response.toString());
    }
    /**
     * 上传同步 视频专网
     * 上传同步 视频专网 -- 公安内网
     * @param url
     * @param entity
     * @return
@@ -182,32 +103,6 @@
        return responseBody;
    }
    /**
     * 上传同步2
     * @param url
     * @param entity
     * @return
     * @throws IOException
     */
    public static String sendPostRequestTwo(String url, SynchronousRequest entity) throws IOException {
        org.apache.http.client.HttpClient httpClient = HttpClientBuilder.create().build();
        HttpPost httpPost = new HttpPost(url);
        ContentType contentType = ContentType.create("multipart/form-data", StandardCharsets.UTF_8);
        MultipartEntityBuilder builder = MultipartEntityBuilder.create();
        builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
        // 添加MultipartFile参数
        builder.addBinaryBody("file", entity.getFile().getBytes(), ContentType.MULTIPART_FORM_DATA, entity.getFile().getOriginalFilename());
        HttpEntity multipart = builder.build();
        httpPost.setEntity(multipart);
        HttpResponse response = httpClient.execute(httpPost);
        String responseBody = EntityUtils.toString(response.getEntity());
        System.out.println("Response: " + responseBody);
        return responseBody;
    }
    /**
     * 删除同步
@@ -236,5 +131,56 @@
        System.out.println("Response: " + responseBody);
        return responseBody;
    }
    /**
     * 上传同步 视频专网 -- 公安内网(定时)
     * @param url
     * @param entity
     * @return
     * @throws IOException
     */
    public static String sendPostRequest2(String url, SynchronousRequest entity, byte[] bytes) throws IOException {
        org.apache.http.client.HttpClient httpClient = HttpClientBuilder.create().build();
        HttpPost httpPost = new HttpPost(url);
        ContentType contentType = ContentType.create("multipart/form-data", StandardCharsets.UTF_8);
        MultipartEntityBuilder builder = MultipartEntityBuilder.create();
        builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
        // 添加MultipartFile参数
        builder.addBinaryBody("file", bytes, ContentType.MULTIPART_FORM_DATA, entity.getFileName());
        // 添加String参数
        builder.addTextBody("path", entity.getPath(), ContentType.TEXT_PLAIN);
        builder.addTextBody("ossId", entity.getOssId(), ContentType.TEXT_PLAIN);
        builder.addTextBody("createBy", entity.getCreateBy(), ContentType.TEXT_PLAIN);
        builder.addTextBody("password", entity.getPassword(), ContentType.TEXT_PLAIN);
        builder.addTextBody("fileName", entity.getFileName(), contentType);
        builder.addTextBody("createTime", entity.getCreateTime(), ContentType.TEXT_PLAIN);
        HttpEntity multipart = builder.build();
        httpPost.setEntity(multipart);
        HttpResponse response = httpClient.execute(httpPost);
        String responseBody = EntityUtils.toString(response.getEntity());
        System.out.println("Response: " + responseBody);
        return responseBody;
    }
    /**
     * @param input 输入流
     * @return byte[] 数组
     */
    public static byte[] inputStream2byte(InputStream input) throws IOException {
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        byte[] buffer = new byte[4096];
        int n = 0;
        while (-1 != (n = input.read(buffer))) {
            output.write(buffer, 0, n);
        }
        return output.toByteArray();
    }
}