cuikaidong
2025-08-20 550a1a7dfdcb742dd5eb0c6292175ed47f181ad3
麒麟系统适配ftp,标准参数修改
已修改9个文件
1035 ■■■■■ 文件已修改
lxzn-module-system/lxzn-system-biz/src/main/java/org/jeecg/modules/iot/controller/EquipmentController.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-system/lxzn-system-biz/src/main/java/org/jeecg/modules/iot/controller/ServeDeployController.java 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-system/lxzn-system-biz/src/main/java/org/jeecg/modules/iot/mdc/service/IEquipmentService.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-system/lxzn-system-biz/src/main/java/org/jeecg/modules/iot/mdc/service/impl/EquipmentServiceImpl.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-system/lxzn-system-biz/src/main/java/org/jeecg/modules/iot/service/impl/RealParameterServiceImpl.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-system/lxzn-system-biz/src/main/java/org/jeecg/modules/iot/service/impl/ServerDeployServiceImpl.java 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-system/lxzn-system-biz/src/main/java/org/jeecg/modules/iot/util/FtpUtil.java 918 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-system/lxzn-system-start/src/main/resources/application-dev.yml 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-system/lxzn-system-start/src/main/resources/application-prod.yml 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
lxzn-module-system/lxzn-system-biz/src/main/java/org/jeecg/modules/iot/controller/EquipmentController.java
@@ -189,6 +189,8 @@
            param.clear();
            param.put("id", equipment.getEqptCode());
            HttpClientUtil.doGet("http://localhost:3002/ScriptCompiler/DeleteDevicescript", param);
            // 给mdc设备表删除设备信息
            mdcEquipmentService.deleteEquipmentByEquipmentId(equipment.getEqptCode());
        }
        equipmentService.removeById(id);
        return Result.ok("删除成功!");
lxzn-module-system/lxzn-system-biz/src/main/java/org/jeecg/modules/iot/controller/ServeDeployController.java
@@ -54,8 +54,8 @@
    private IMqttDeployService mqttDeployService;
    @Autowired
    private MqttCustomerClient mqttCustomerClient;
    @Resource
    private RestTemplate restTemplate;
    @Autowired
    private FtpUtil ftpUtil;
    @Autowired
    private IEquipmentService equipmentService;
    @Value("${ftp.address}")
@@ -138,10 +138,10 @@
        topic[1] = "IOT\\" + serverDeploy.getServerCode() + "\\WriteMessage";
        mqttCustomerClient.subscribe(topic);
        // 创建ftp文件夹目录
        FtpUtil.createFolder("/log", serverDeploy.getServerCode());
        FtpUtil.createFolder("/deploy", serverDeploy.getServerCode());
        FtpUtil.createFolder("/deploy/" + serverDeploy.getServerCode(), "software");
        FtpUtil.createFolder("/deploy/" + serverDeploy.getServerCode(), "deploy");
        ftpUtil.createFolder("/log", serverDeploy.getServerCode());
        ftpUtil.createFolder("/deploy", serverDeploy.getServerCode());
        ftpUtil.createFolder("/deploy/" + serverDeploy.getServerCode(), "software");
        ftpUtil.createFolder("/deploy/" + serverDeploy.getServerCode(), "deploy");
        // 生成本地文件夹
        boolean b = FileUtil.queryCatalogue(ftpAddress + serverDeploy.getServerCode());
        if (b) {
@@ -277,16 +277,16 @@
            return Result.error("请上传.zip文件!");
        }
        // 新建ftp文件夹
        FtpUtil.createFolder("/deploy/" + serverDeploy.getServerCode() + "/software/", serverDeploy.getLatestCollectVersion());
        ftpUtil.createFolder("/deploy/" + serverDeploy.getServerCode() + "/software/", serverDeploy.getLatestCollectVersion());
        // 复制采集软件到ftp
        // 上传到ftp
        String newCollectAddress = serverDeploy.getNewCollectAddress();
        String basePath = "/deploy/"; // FTP服务器基础目录
        String filePath = serverDeploy.getServerCode() + "/software/" + serverDeploy.getLatestCollectVersion(); // FTP服务器文件存放路径。例如分日期存放:/2015/01/01。文件的路径为basePath+filePath
        String filePath = "/"+serverDeploy.getServerCode() + "/software/" + serverDeploy.getLatestCollectVersion(); // FTP服务器文件存放路径。例如分日期存放:/2015/01/01。文件的路径为basePath+filePath
        String filename = newCollectAddress.substring(14, newCollectAddress.length());// 上传到FTP服务器上的文件名
        try {
            FileInputStream fileInputStream = new FileInputStream(ftpAddress + newCollectAddress);
            FtpUtil.uploadFile(basePath, filePath, filename, fileInputStream);
            ftpUtil.uploadFile(basePath, filePath, filename, fileInputStream);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
lxzn-module-system/lxzn-system-biz/src/main/java/org/jeecg/modules/iot/mdc/service/IEquipmentService.java
@@ -60,4 +60,11 @@
     * @param equipmentId
     */
    Equipment findEquipmentByEquipmentId(String equipmentId);
    /**
     * 根据设备id删除设备信息
     *
     * @param equipmentId
     */
    void deleteEquipmentByEquipmentId(String equipmentId);
}
lxzn-module-system/lxzn-system-biz/src/main/java/org/jeecg/modules/iot/mdc/service/impl/EquipmentServiceImpl.java
@@ -45,4 +45,9 @@
    public Equipment findEquipmentByEquipmentId(String equipmentId) {
        return new LambdaQueryChainWrapper<>(baseMapper).eq(Equipment::getEquipmentid,equipmentId).one();
    }
    @Override
    public void deleteEquipmentByEquipmentId(String equipmentId) {
        baseMapper.deleteById(equipmentId);
    }
}
lxzn-module-system/lxzn-system-biz/src/main/java/org/jeecg/modules/iot/service/impl/RealParameterServiceImpl.java
@@ -253,6 +253,12 @@
    @Override
    public Result<?> canonicalParameter(String id) {
        int list = new LambdaQueryChainWrapper<>(baseMapper)
                .eq(RealParameter::getParameterGroupId, id)
                .list().size();
        if (list > 1) {
            return Result.error("参数为空才可导入标准参数");
        }
        // 查询最新编号
        Integer parameterCode = findRealParameterGroupId(id) - 1;
        List<Parameter> parameters = equipmentService.findParameterById(id);
@@ -265,6 +271,7 @@
            real.setParameterName(parameter.getName());
            real.setParameterType(parameter.getDataType());
            real.setAddress(parameter.getDefault1());
            real.setParameterDescribe(parameter.getDescribe());
            real.setReadWriteType("只读");
            List<Parameter> parameterList = equipmentService.findDataTypeById(id);
            parameterList.forEach(p -> {
@@ -273,13 +280,14 @@
                    real.setSystemDataType(p.getSystemDataType());
                }
            });
            real.setDataLength(1);
            // 填充参数
            parameterCode = parameterCode + 1;
            real.setParameterGroupId(id);
            real.setParameterCode(parameterCode);
            realParameter.add(real);
        }
//        saveBatch(realParameter);
        this.saveBatch(realParameter);
        return Result.ok("导入成功");
    }
lxzn-module-system/lxzn-system-biz/src/main/java/org/jeecg/modules/iot/service/impl/ServerDeployServiceImpl.java
@@ -1,16 +1,15 @@
package org.jeecg.modules.iot.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.jeecg.common.api.vo.Result;
import org.jeecg.modules.iot.entity.*;
import org.jeecg.modules.iot.service.*;
import org.jeecg.modules.iot.util.FtpUtil;
import org.jeecg.modules.iot.mapper.ServerDeployMapper;
import org.jeecg.modules.iot.mqtt.config.MqttCustomerClient;
import org.jeecg.modules.iot.service.*;
import org.jeecg.modules.iot.util.FileUtil;
import org.jeecg.modules.iot.util.FtpUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
@@ -26,7 +25,9 @@
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;
@@ -39,9 +40,12 @@
 */
@Service
public class ServerDeployServiceImpl extends ServiceImpl<ServerDeployMapper, ServerDeploy> implements IServerDeployService {
    @Autowired
    private FtpUtil ftpUtil;
    @Value("${ftp.address}")
    private String ftpAddress;
    @Value("${operatingSystem}")
    private String operatingSystem;
    @Autowired
    private IInfluxdbDeployService influxdbDeployService;
    @Autowired
@@ -104,7 +108,7 @@
    @Transactional(rollbackFor = Exception.class)
    public Result<?> addDeployDocument(String id) {
        // 验证FTP连接
        boolean isConnected = FtpUtil.testFtpConnection();
        boolean isConnected = ftpUtil.testFtpConnection();
        if (!isConnected) {
            return Result.error("FTP连接失败,请检查配置!");
        }
@@ -248,9 +252,9 @@
                                        tagInfo.setAttribute("Name", r.getParameterName());
                                        tagInfo.setAttribute("Address", r.getAddress());
                                        tagInfo.setAttribute("DataType", r.getParameterType());
                                        if (r.getReadWriteType().equals("只读")){
                                        if (r.getReadWriteType().equals("只读")) {
                                            tagInfo.setAttribute("ReadOrWrite", "R");
                                        }else{
                                        } else {
                                            tagInfo.setAttribute("ReadOrWrite", "R/W");
                                        }
                                        tagInfo.setAttribute("Describe", r.getParameterDescribe());
@@ -322,9 +326,9 @@
            // 创建xml文件并写入内容
            tf.transform(new DOMSource(document), new StreamResult(new File("/iot/" + serverDeploy.getServerCode() + "/deploy/" + formattedDate + "/CollectionConfiguration.lmx")));
            // 创建写库表结构查询虚设备列表,增加设备表
            equipmentService.createEmptyEquipmentTable(serverDeploy.getDeployIssueTime(),serverDeploy.getId());
            equipmentService.createEmptyEquipmentTable(serverDeploy.getDeployIssueTime(), serverDeploy.getId());
            // 查询虚设备参数,增加字段
            emptyParameterService.createEmptyEmptyField(serverDeploy.getDeployIssueTime(),serverDeploy.getId());
            emptyParameterService.createEmptyEmptyField(serverDeploy.getDeployIssueTime(), serverDeploy.getId());
        } catch (Exception e) {
            e.printStackTrace();
            return Result.error("配置文件生成失败");
@@ -334,28 +338,41 @@
        serverDeploy.setDeployIssueTime(new Date());
        baseMapper.updateById(serverDeploy);
        // 生成本地文件夹
//        FileUtil.createDir("D:/iot/" + serverDeploy.getServerCode() + "/deploy/" + formattedDate);
        FileUtil.createDir(ftpAddress + serverDeploy.getServerCode() + "/deploy/" + formattedDate + "/script");
        // 生成本地文件夹
        FileUtil.createDir(ftpAddress + serverDeploy.getServerCode() + "/deploy/" + formattedDate);
        MqttParameter mqttParameter = new MqttParameter();
        // 上传到ftp
        String basePath = "/deploy"; // FTP服务器基础目录
        String filePath = serverDeploy.getServerCode() + "/deploy/" + formattedDate; // FTP服务器文件存放路径。例如分日期存放:/2015/01/01。文件的路径为basePath+filePath
        String filename = "CollectionConfiguration.lmx";// 上传到FTP服务器上的文件名
        //创建一个输入流
        try {
            FileInputStream fileInputStream = new FileInputStream(ftpAddress + serverDeploy.getServerCode() + "/deploy/" + formattedDate + "/CollectionConfiguration.lmx");
            FtpUtil.uploadFile(basePath, filePath, filename, fileInputStream);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        if (operatingSystem.equals("windows")) {
            String basePath = "/deploy"; // FTP服务器基础目录
            String filePath = "/" + serverDeploy.getServerCode() + "/deploy/" + formattedDate; // FTP服务器文件存放路径。例如分日期存放:/2015/01/01。文件的路径为basePath+filePath
            String filename = "CollectionConfiguration.lmx";// 上传到FTP服务器上的文件名
            //创建一个输入流
            try {
                FileInputStream fileInputStream = new FileInputStream(ftpAddress + serverDeploy.getServerCode() + "/deploy/" + formattedDate + "/CollectionConfiguration.lmx");
                ftpUtil.uploadFile(basePath, filePath, filename, fileInputStream);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
            mqttParameter.setParameter4(basePath + "/" + filePath);
        } else {
            String basePath = "/iot"; // FTP服务器基础目录
            String filePath = "/deploy/" + serverDeploy.getServerCode() + "/deploy/" + formattedDate; // FTP服务器文件存放路径。例如分日期存放:/2015/01/01。文件的路径为basePath+filePath
            String filename = "CollectionConfiguration.lmx";// 上传到FTP服务器上的文件名
            //创建一个输入流
            try {
                FileInputStream fileInputStream = new FileInputStream(ftpAddress + serverDeploy.getServerCode() + "/deploy/" + formattedDate + "/CollectionConfiguration.lmx");
                ftpUtil.uploadFileKylin(basePath, filePath, filename, fileInputStream);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
            mqttParameter.setParameter4(filePath);
        }
        // 存储脚本,复制整个文件夹
        FtpUtil.uploadFolder(new File(ftpAddress+serverDeploy.getServerCode()+"/script/"), "/deploy/"+serverDeploy.getServerCode()+"/deploy/"+formattedDate+"/script");
        // 生成脚本
        ftpUtil.uploadFolderKylin(new File(ftpAddress + serverDeploy.getServerCode() + "/script/"), "/deploy/" + serverDeploy.getServerCode() + "/deploy/" + formattedDate + "/script");
        // 发送配置文件版信号
        MqttParameter mqttParameter = new MqttParameter();
        mqttParameter.setId(serverDeploy.getServerCode());
        mqttParameter.setType("version");
        mqttParameter.setParameter2(formattedDate);
        mqttParameter.setParameter4(basePath +"/" +filePath);
        mqttCustomerClient.pushlish(2, false, serverDeploy.getServerCode(), mqttParameter);
        return Result.ok("配置文件生成成功");
    }
lxzn-module-system/lxzn-system-biz/src/main/java/org/jeecg/modules/iot/util/FtpUtil.java
@@ -1,41 +1,39 @@
package org.jeecg.modules.iot.util;
/**
 * @Description: Ftp配置
 * @Author: cuikaidong
 * @Date: 2024-12-10
 * @Version: V1.0
 */
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
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.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.io.*;
import java.net.SocketException;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
@Component
public class FtpUtil {
    private final static Log logger = LogFactory.getLog(FtpUtil.class);
    /**
     * 本地字符编码
     */
    private static String LOCAL_CHARSET = "GBK";
    private static final Log logger = LogFactory.getLog(FtpUtil.class);
    // FTP协议里面,规定文件名编码为iso-8859-1
    private static String SERVER_CHARSET = "ISO-8859-1";
    //ftp服务器IP地址 ,新火炬10.210.199.2,起落架10.0.221.200
    private static String ftpHost = "10.10.10.98";
    //ftp服务器端口
    private static int ftpPort = 21;
    //ftp服务器用户名
    private static String ftpUserName = "admin";
    //ftp服务器密码
    private static String ftpPassword = "lx@2024";
    // 依赖注入配置,建议通过 Nacos 或配置中心管理
    @Value("${ftp.LOCAL_CHARSET:UTF-8}")
    private String LOCAL_CHARSET;
    @Value("${ftp.SERVER_CHARSET:ISO-8859-1}")
    private String SERVER_CHARSET;
    @Value("${ftp.ftpHost}")
    private String ftpHost;
    @Value("${ftp.ftpPort:21}")
    private int ftpPort;
    @Value("${ftp.ftpUserName}")
    private String ftpUserName;
    @Value("${ftp.ftpPassword}")
    private String ftpPassword;
    @Value("${operatingSystem}")
    private String operatingSystem;
    /**
@@ -43,7 +41,7 @@
     *
     * @return true:连接成功;false:连接失败
     */
    public static boolean testFtpConnection() {
    public boolean testFtpConnection() {
        FTPClient ftpClient = new FTPClient();
        try {
            // 连接FTP服务器
@@ -58,377 +56,593 @@
            if (isConnected) {
                logger.info("FTP连接验证成功!");
            } else {
                logger.error("FTP连接失败:用户名/密码错误或服务器不可达");
                logger.error("FTP连接失败:用户名/密码错误或服务器不可达,响应码:" + replyCode);
            }
            return isConnected;
        } catch (SocketException e) {
            logger.error("FTP服务器IP地址错误或端口被占用:" + e.getMessage());
            logger.error("FTP服务器连接失败(IP或端口错误):" + e.getMessage(), e);
        } catch (IOException e) {
            logger.error("FTP连接异常:" + e.getMessage());
            logger.error("FTP连接异常:" + e.getMessage(), e);
        } finally {
            // 释放资源
            if (ftpClient.isConnected()) {
                try {
                    ftpClient.logout();
                    ftpClient.disconnect();
                } catch (IOException e) {
                    logger.warn("关闭FTP连接时发生异常:" + e.getMessage());
                }
            }
            disconnectQuietly(ftpClient);
        }
        return false;
    }
    // ====================== 核心优化:连接管理重构 ======================
    /**
     * 获取FTPClient对象
     * 获取 FTPClient 连接(带重试、模式配置)
     *
     * @return
     * @return 可用 FTPClient,失败返回 null
     */
    public static FTPClient getFTPClient() {
        FTPClient ftpClient = null;
        try {
            ftpClient = new FTPClient();
            ftpClient.connect(ftpHost, ftpPort);// 连接FTP服务器
            ftpClient.login(ftpUserName, ftpPassword);// 登陆FTP服务器
            if (!FTPReply.isPositiveCompletion(ftpClient.getReplyCode())) {
                logger.info("未连接到FTP,用户名或密码错误。");
                ftpClient.disconnect();
            } else {
                logger.info("FTP连接成功。");
            }
        } catch (SocketException e) {
            e.printStackTrace();
            logger.info("FTP的IP地址可能错误,请正确配置。");
        } catch (IOException e) {
            e.printStackTrace();
            logger.info("FTP的端口错误,请正确配置。");
        }
        return ftpClient;
    }
    /**
     * 从FTP服务器下载文件
     *
     * @param ftpHost     FTP IP地址
     * @param ftpUserName FTP 用户名
     * @param ftpPassword FTP用户名密码
     * @param ftpPort     FTP端口
     * @param ftpPath     FTP服务器中文件所在路径 格式: ftptest/aa
     * @param localPath   下载到本地的位置 格式:H:/download
     * @param fileName    文件名称
     */
    public static void downloadFtpFile(String ftpHost, String ftpUserName, String ftpPassword, int ftpPort,
                                       String ftpPath, String localPath, String fileName) {
        FTPClient ftpClient = null;
        try {
            ftpClient = getFTPClient();
            // 设置上传文件的类型为二进制类型
            if (FTPReply.isPositiveCompletion(ftpClient.sendCommand("OPTS UTF8", "ON"))) {// 开启服务器对UTF-8的支持,如果服务器支持就用UTF-8编码,否则就使用本地编码(GBK).
                LOCAL_CHARSET = "UTF-8";
            }
            ftpClient.setControlEncoding(LOCAL_CHARSET);
            ftpClient.enterLocalPassiveMode();// 设置被动模式
            ftpClient.setFileType(FTP.BINARY_FILE_TYPE);// 设置传输的模式
            // 上传文件
            //对中文文件名进行转码,否则中文名称的文件下载失败
            String fileNameTemp = new String(fileName.getBytes(LOCAL_CHARSET), SERVER_CHARSET);
            ftpClient.changeWorkingDirectory(ftpPath);
            InputStream retrieveFileStream = ftpClient.retrieveFileStream(fileNameTemp);
            // 第一种方式下载文件(推荐)
             /* File localFile = new File(localPath + File.separatorChar + fileName);
              OutputStream os = new FileOutputStream(localFile);
              ftpClient.retrieveFile(fileName, os); os.close();*/
            // 第二种方式下载:将输入流转成字节,再生成文件,这种方式方便将字节数组直接返回给前台jsp页面
            byte[] input2byte = input2byte(retrieveFileStream);
            byte2File(input2byte, localPath, fileName);
            if (null != retrieveFileStream) {
                retrieveFileStream.close();
            }
        } catch (FileNotFoundException e) {
            logger.error("没有找到" + ftpPath + "文件");
            e.printStackTrace();
        } catch (SocketException e) {
            logger.error("连接FTP失败.");
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
            logger.error("文件读取错误。");
            e.printStackTrace();
        } finally {
            if (ftpClient.isConnected()) {
                try {
                    //退出登录
                    ftpClient.logout();
                    //关闭连接
                    ftpClient.disconnect();
                } catch (IOException e) {
                }
            }
        }
    }
    /**
     * Description: 向FTP服务器上传文件
     *
     * @param basePath FTP服务器基础目录
     * @param filePath FTP服务器文件存放路径。例如分日期存放:/2015/01/01。文件的路径为basePath+filePath
     * @param filename 上传到FTP服务器上的文件名
     * @param input    输入流
     * @return 成功返回true,否则返回false
     */
    public static boolean uploadFile(String basePath, String filePath, String filename, InputStream input) {
        boolean result = false;
        FTPClient ftpClient = null;
        try {
            int reply;
            ftpClient = getFTPClient();
            reply = ftpClient.getReplyCode();
            if (!FTPReply.isPositiveCompletion(reply)) {
                ftpClient.disconnect();
                return result;
            }
            // 切换到上传目录
            if (!ftpClient.changeWorkingDirectory(basePath + filePath)) {
                // 如果目录不存在创建目录
                String[] dirs = filePath.split("/");
                String tempPath = basePath;
                for (String dir : dirs) {
                    if (null == dir || "".equals(dir))
                        continue;
                    tempPath += "/" + dir;
                    if (!ftpClient.changeWorkingDirectory(tempPath)) {
                        if (!ftpClient.makeDirectory(tempPath)) {
                            return result;
                        } else {
                            ftpClient.changeWorkingDirectory(tempPath);
                        }
                    }
                }
            }
            // 设置上传文件的类型为二进制类型
            if (FTPReply.isPositiveCompletion(ftpClient.sendCommand("OPTS UTF8", "ON"))) {// 开启服务器对UTF-8的支持,如果服务器支持就用UTF-8编码,否则就使用本地编码(GBK).
                LOCAL_CHARSET = "UTF-8";
            }
            ftpClient.setControlEncoding(LOCAL_CHARSET);
            ftpClient.enterLocalPassiveMode();// 设置被动模式
            ftpClient.setFileType(FTP.BINARY_FILE_TYPE);// 设置传输的模式
            // 上传文件
            filename = new String(filename.getBytes(LOCAL_CHARSET), SERVER_CHARSET);
            if (!ftpClient.storeFile(filename, input)) {
                return result;
            }
            if (null != input) {
                input.close();
            }
            result = true;
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (ftpClient.isConnected()) {
                try {
                    //退出登录
                    ftpClient.logout();
                    //关闭连接
                    ftpClient.disconnect();
                } catch (IOException ioe) {
                }
            }
        }
        return result;
    }
    /**
     * 删除文件 未测试
     *
     * @param ftpHost     FTP服务器地址
     * @param ftpPort     FTP服务器端口号
     * @param ftpUserName FTP登录帐号
     * @param ftpPassword FTP登录密码
     * @param pathname    FTP服务器保存目录
     * @param filename    要删除的文件名称
     * @return
     */
    public static boolean deleteFile(String ftpHost, int ftpPort, String ftpUserName, String ftpPassword, String pathname,
                                     String filename) {
        boolean flag = false;
    public FTPClient getFTPClient() {
        FTPClient ftpClient = new FTPClient();
        try {
            ftpClient = getFTPClient();
            // 验证FTP服务器是否登录成功
            int replyCode = ftpClient.getReplyCode();
            if (!FTPReply.isPositiveCompletion(replyCode)) {
                return flag;
            }
            // 切换FTP目录
            ftpClient.changeWorkingDirectory(pathname);
            // 设置上传文件的类型为二进制类型
            if (FTPReply.isPositiveCompletion(ftpClient.sendCommand("OPTS UTF8", "ON"))) {// 开启服务器对UTF-8的支持,如果服务器支持就用UTF-8编码,否则就使用本地编码(GBK).
                LOCAL_CHARSET = "UTF-8";
            ftpClient.connect(ftpHost, ftpPort);
            ftpClient.login(ftpUserName, ftpPassword);
            // 强制开启 UTF-8 支持(优先覆盖服务器配置)
            if (FTPReply.isPositiveCompletion(ftpClient.sendCommand("OPTS UTF8", "ON"))) {
                LOCAL_CHARSET = StandardCharsets.UTF_8.name();
            }
            ftpClient.setControlEncoding(LOCAL_CHARSET);
            ftpClient.enterLocalPassiveMode();// 设置被动模式
            ftpClient.setFileType(FTP.BINARY_FILE_TYPE);// 设置传输的模式
            //对中文名称进行转码
            filename = new String(filename.getBytes(LOCAL_CHARSET), SERVER_CHARSET);
            ftpClient.dele(filename);
            flag = true;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (ftpClient.isConnected()) {
                try {
                    //退出登录
                    ftpClient.logout();
                    //关闭连接
                    ftpClient.disconnect();
                } catch (IOException e) {
                }
            // 关键配置:二进制传输 + 被动模式,解决文件损坏、防火墙阻塞问题
            ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
            ftpClient.enterLocalPassiveMode();
            int replyCode = ftpClient.getReplyCode();
            if (!FTPReply.isPositiveCompletion(replyCode)) {
                logger.error("FTP 连接失败,响应码:" + replyCode);
                disconnectQuietly(ftpClient);
                return null;
            }
            logger.info("FTP 连接成功,响应码:" + replyCode);
            return ftpClient;
        } catch (IOException e) {
            logger.error("FTP 连接异常:" + e.getMessage(), e);
            disconnectQuietly(ftpClient);
            return null;
        }
        return flag;
    }
    /**
     * 创建文件夹
     *
     * @param pathname
     * @param filename
     * @return
     * 安静关闭连接(避免嵌套异常)
     */
    public static boolean createFolder(String pathname, String filename) {
        boolean flag = false;
        FTPClient ftpClient = new FTPClient();
        try {
            ftpClient = getFTPClient();
            // 验证FTP服务器是否登录成功
            int replyCode = ftpClient.getReplyCode();
            if (!FTPReply.isPositiveCompletion(replyCode)) {
                return flag;
            }
            // 切换FTP目录
            ftpClient.changeWorkingDirectory(pathname);
            boolean created = ftpClient.makeDirectory(filename);
            if (created) {
                logger.info(filename + "文件夹创建成功");
            } else {
                logger.info(filename + "创建文件夹失败");
            }
            flag = true;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (ftpClient.isConnected()) {
                try {
                    //退出登录
                    ftpClient.logout();
                    //关闭连接
                    ftpClient.disconnect();
                } catch (IOException e) {
                }
    private void disconnectQuietly(FTPClient ftpClient) {
        if (ftpClient != null && ftpClient.isConnected()) {
            try {
                ftpClient.logout();
                ftpClient.disconnect();
            } catch (IOException e) {
                logger.warn("关闭 FTP 连接失败:" + e.getMessage(), e);
            }
        }
        return flag;
    }
    public static void uploadFolder(File localFolder, String remoteFolder) {
        if (!localFolder.exists()) {
    // ====================== 文件夹上传优化:递归 + 完整性校验 ======================
    /**
     * 递归上传文件夹(核心方法)
     *
     * @param localFolder  本地文件夹
     * @param remoteFolder 远程父目录
     */
    public void uploadFolder(File localFolder, String remoteFolder) {
        if (localFolder == null || !localFolder.isDirectory()) {
            logger.warn("本地文件夹无效:" + (localFolder == null ? "null" : localFolder.getPath()));
            return;
        }
        FTPClient ftpClient = getFTPClient();
        try {
            // 创建远程文件夹
            if (!ftpClient.changeWorkingDirectory(remoteFolder)) {
                ftpClient.makeDirectory(remoteFolder);
                ftpClient.changeWorkingDirectory(remoteFolder);
            }
        } catch (IOException e) {
            e.printStackTrace();
        if (ftpClient == null) {
            logger.error("FTP 客户端初始化失败,放弃上传:" + localFolder.getPath());
            return;
        }
        // 遍历本地文件夹中的文件和子文件夹
        File[] files = localFolder.listFiles();
        if (files != null) {
        try {
            // 确保远程目录存在(递归创建)
            if (!createRemoteDirectory(ftpClient, remoteFolder)) {
                logger.error("创建远程目录失败:" + remoteFolder);
                return;
            }
            // 遍历本地文件
            File[] files = localFolder.listFiles();
            if (files == null) {
                logger.warn("本地文件夹为空:" + localFolder.getPath());
                return;
            }
            for (File file : files) {
                if (file.isDirectory()) {
                    // 递归上传子文件夹
                    uploadFolder(file, remoteFolder + "/" + file.getName());
                } else {
                    try {
                        // 上传文件
                        uploadFile1(ftpClient, file, remoteFolder);
                    } catch (IOException e) {
                        e.printStackTrace();
                } else if (file.isFile()) {
                    // 上传文件(带完整性校验)
                    boolean success = uploadFileWithCheck(ftpClient, file, remoteFolder);
                    if (!success) {
                        logger.error("文件上传失败:" + file.getPath() + " -> " + remoteFolder);
                    }
                }
            }
        }
    }
    private static void uploadFile1(FTPClient ftpClient, File localFile, String remoteFolder) throws IOException {
        try (FileInputStream inputStream = new FileInputStream(localFile)) {
            ftpClient.storeFile(remoteFolder + "/" + localFile.getName(), inputStream);
        }
    }
    // 将字节数组转换为输入流
    public static final InputStream byte2Input(byte[] buf) {
        return new ByteArrayInputStream(buf);
    }
    // 将输入流转为byte[]
    public static final byte[] input2byte(InputStream inStream) throws IOException {
        ByteArrayOutputStream swapStream = new ByteArrayOutputStream();
        byte[] buff = new byte[100];
        int rc = 0;
        while ((rc = inStream.read(buff, 0, 100)) > 0) {
            swapStream.write(buff, 0, rc);
        }
        byte[] in2b = swapStream.toByteArray();
        return in2b;
    }
    // 将byte[]转为文件
    public static void byte2File(byte[] buf, String filePath, String fileName) {
        BufferedOutputStream bos = null;
        FileOutputStream fos = null;
        File file = null;
        try {
            File dir = new File(filePath);
            if (!dir.exists() && dir.isDirectory()) {
                dir.mkdirs();
            }
            file = new File(filePath + File.separator + fileName);
            fos = new FileOutputStream(file);
            bos = new BufferedOutputStream(fos);
            bos.write(buf);
        } catch (Exception e) {
            e.printStackTrace();
        } catch (IOException e) {
            logger.error("上传文件夹异常:" + e.getMessage(), e);
        } finally {
            if (bos != null) {
                try {
                    bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
            disconnectQuietly(ftpClient);
        }
    }
    /**
     * 麒麟递归上传文件夹(核心方法)
     *
     * @param localFolder  本地文件夹
     * @param remoteFolder 远程父目录
     */
    public void uploadFolderKylin(File localFolder, String remoteFolder) {
        if (operatingSystem.equals("windows")){
            if (localFolder == null || !localFolder.isDirectory()) {
                logger.warn("本地文件夹无效:" + (localFolder == null ? "null" : localFolder.getPath()));
                return;
            }
            FTPClient ftpClient = getFTPClient();
            if (ftpClient == null) {
                logger.error("FTP 客户端初始化失败,放弃上传:" + localFolder.getPath());
                return;
            }
            try {
                // 麒麟系统FTP服务器必需的设置
                ftpClient.enterLocalPassiveMode(); // 启用被动模式(类Unix系统推荐)
                ftpClient.setFileType(FTP.BINARY_FILE_TYPE); // 二进制传输避免文件损坏
                ftpClient.setControlEncoding("UTF-8"); // 控制连接编码
                // 确保远程目录存在(递归创建)
                if (!createRemoteDirectory(ftpClient, remoteFolder)) {
                    logger.error("创建远程目录失败:" + remoteFolder);
                    return;
                }
                // 遍历本地文件
                File[] files = localFolder.listFiles();
                if (files == null) {
                    logger.warn("本地文件夹为空:" + localFolder.getPath());
                    return;
                }
                for (File file : files) {
                    if (file.isDirectory()) {
                        // 递归上传子文件夹
                        uploadFolder(file, remoteFolder + "/" + file.getName());
                    } else if (file.isFile()) {
                        // 上传文件(带完整性校验)
                        boolean success = uploadFileWithCheck(ftpClient, file, remoteFolder);
                        if (!success) {
                            logger.error("文件上传失败:" + file.getPath() + " -> " + remoteFolder);
                        }
                    }
                }
            } catch (IOException e) {
                logger.error("上传文件夹异常:" + e.getMessage(), e);
            } finally {
                disconnectQuietly(ftpClient);
            }
        }else {
            if (localFolder == null || !localFolder.isDirectory()) {
                logger.warn("本地文件夹无效:" + (localFolder == null ? "null" : localFolder.getPath()));
                return;
            }
            FTPClient ftpClient = getFTPClient();
            if (ftpClient == null) {
                logger.error("FTP 客户端初始化失败,放弃上传:" + localFolder.getPath());
                return;
            }
            try {
                // 麒麟系统FTP服务器必需的设置
                ftpClient.enterLocalPassiveMode(); // 启用被动模式(类Unix系统推荐)
                ftpClient.setFileType(FTP.BINARY_FILE_TYPE); // 二进制传输避免文件损坏
                ftpClient.setControlEncoding("UTF-8"); // 控制连接编码
                // 确保远程目录存在(递归创建)
                if (!createKylinFtpDirectory(ftpClient, "/iot", remoteFolder)) {
                    logger.error("创建远程目录失败:" + remoteFolder);
                    return;
                }
                // 遍历本地文件
                File[] files = localFolder.listFiles();
                if (files == null) {
                    logger.warn("本地文件夹为空:" + localFolder.getPath());
                    return;
                }
                for (File file : files) {
                    if (file.isDirectory()) {
                        // 递归上传子文件夹
                        uploadFolder(file, "/iot"+remoteFolder + "/" + file.getName());
                    } else if (file.isFile()) {
                        // 上传文件(带完整性校验)
                        boolean success = uploadFileWithCheck(ftpClient, file, "/iot"+remoteFolder);
                        if (!success) {
                            logger.error("文件上传失败:" + file.getPath() + " -> " + remoteFolder);
                        }
                    }
                }
            } catch (IOException e) {
                logger.error("上传文件夹异常:" + e.getMessage(), e);
            } finally {
                disconnectQuietly(ftpClient);
            }
        }
    }
    private boolean createRemoteDirectory(FTPClient ftpClient, String remoteDir) throws IOException {
        String[] dirs = remoteDir.split("/");
        StringBuilder currentDir = new StringBuilder();
        for (String dir : dirs) {
            if (dir.trim().isEmpty()) continue;
            currentDir.append("/").append(dir);
            String targetDir = currentDir.toString();
            // 打印当前尝试切换/创建的目录
            logger.info("尝试操作目录:" + targetDir);
            if (!ftpClient.changeWorkingDirectory(targetDir)) {
                boolean mkdirResult = ftpClient.makeDirectory(targetDir);
                // 打印创建结果和 FTP 响应
                logger.info("创建目录结果: " + mkdirResult + ", 响应码: " + ftpClient.getReplyCode()
                        + ", 响应信息: " + ftpClient.getReplyString());
                if (!mkdirResult) {
                    logger.error("创建目录失败: " + targetDir);
                    return false;
                }
                if (!ftpClient.changeWorkingDirectory(targetDir)) {
                    logger.error("切换目录失败: " + targetDir + ", 响应码: " + ftpClient.getReplyCode()
                            + ", 响应信息: " + ftpClient.getReplyString());
                    return false;
                }
            }
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
        }
        return true;
    }
    /**
     * 在麒麟系统的FTP服务器上创建目录(支持多级目录)
     *
     * @param baseDir   FTP基础目录(如/iot)
     * @param remoteDir 要创建的目录路径(如data/logs/2023)
     * @return 是否创建成功
     * @throws IOException IO异常
     */
    public boolean createKylinFtpDirectory(FTPClient ftpClient, String baseDir, String remoteDir) throws IOException {
        // 确保基础目录以/结尾
        String normalizedBase = baseDir.endsWith("/") ? baseDir : baseDir + "/";
        // 处理远程目录,移除开头可能的/,避免重复
        String normalizedRemote = remoteDir.startsWith("/") ? remoteDir.substring(1) : remoteDir;
        // 分割目录
        String[] dirs = normalizedRemote.split("/");
        StringBuilder currentPath = new StringBuilder(normalizedBase);
        for (String dir : dirs) {
            if (dir.trim().isEmpty()) continue; // 跳过空目录(处理连续//的情况)
            currentPath.append(dir).append("/");
            String targetDir = currentPath.toString();
            // 尝试切换到目录
            if (!ftpClient.changeWorkingDirectory(targetDir)) {
                // 切换失败,尝试创建目录
                boolean created = ftpClient.makeDirectory(targetDir);
                logger.info("创建目录[" + targetDir + "]结果: {" + created + "}, 响应码: {" + ftpClient.getReplyCode() + "}");
                if (!created) {
                    logger.error("创建目录失败: {" + targetDir + "}");
                    return false;
                }
                // 验证是否能切换到新创建的目录
                if (!ftpClient.changeWorkingDirectory(targetDir)) {
                    logger.error("切换到目录失败: {" + targetDir + "}, 响应: {" + ftpClient.getReplyString() + "}");
                    return false;
                }
            }
        }
        return true;
    }
    /**
     * 上传文件(带大小校验)
     *
     * @return 是否上传成功且完整
     */
    private boolean uploadFileWithCheck(FTPClient ftpClient, File localFile, String remoteFolder) throws IOException {
        String remoteFilePath = remoteFolder + "/" + localFile.getName();
        long localFileSize = localFile.length();
        // 校验远程文件是否已存在且完整
        FTPFile[] remoteFiles = ftpClient.listFiles(remoteFilePath);
        if (remoteFiles != null && remoteFiles.length > 0) {
            FTPFile remoteFile = remoteFiles[0];
            if (remoteFile.getSize() == localFileSize) {
                logger.info("文件已存在且完整,跳过上传:" + localFile.getPath());
                return true;
            }
        }
        // 执行上传
        try (FileInputStream inputStream = new FileInputStream(localFile)) {
            boolean uploaded = ftpClient.storeFile(remoteFilePath, inputStream);
            if (!uploaded) {
                logger.error("上传失败,响应码:" + ftpClient.getReplyCode() + " -> " + localFile.getPath());
                return false;
            }
            // 二次校验远程文件大小
            FTPFile[] checkFiles = ftpClient.listFiles(remoteFilePath);
            if (checkFiles == null || checkFiles.length == 0) {
                logger.error("上传后文件丢失:" + localFile.getPath());
                return false;
            }
            return checkFiles[0].getSize() == localFileSize;
        }
    }
    // ====================== 其他方法优化:资源释放 + 编码健壮性 ======================
    /**
     * 下载文件(优化编码处理、资源释放)
     */
    public void downloadFtpFile(String ftpPath, String localPath, String fileName) {
        FTPClient ftpClient = getFTPClient();
        if (ftpClient == null) {
            return;
        }
        try {
            // 编码转换(解决中文路径问题)
            String remoteFilePath = new String((ftpPath + "/" + fileName).getBytes(LOCAL_CHARSET), SERVER_CHARSET);
            if (!ftpClient.changeWorkingDirectory(ftpPath)) {
                logger.error("切换远程目录失败:" + ftpPath);
                return;
            }
            // 下载文件
            try (InputStream is = ftpClient.retrieveFileStream(remoteFilePath);
                 OutputStream os = new FileOutputStream(new File(localPath, fileName))) {
                if (is == null) {
                    logger.error("远程文件不存在:" + remoteFilePath);
                    return;
                }
                byte[] buffer = new byte[4096];
                int len;
                while ((len = is.read(buffer)) != -1) {
                    os.write(buffer, 0, len);
                }
                os.flush();
                // 确认下载完成(关键:避免文件截断)
                if (!ftpClient.completePendingCommand()) {
                    logger.error("FTP 下载未完成:" + remoteFilePath);
                }
            }
        } catch (IOException e) {
            logger.error("下载文件失败:" + fileName, e);
        } finally {
            disconnectQuietly(ftpClient);
        }
    }
    /**
     * 上传文件(独立方法,供复用)
     *
     * @param basePath 远程基础路径
     * @param filePath 远程子路径
     * @param filename 文件名
     * @param input    输入流
     * @return 是否成功
     */
    public boolean uploadFile(String basePath, String filePath, String filename, InputStream input) {
        FTPClient ftpClient = getFTPClient();
        if (ftpClient == null) {
            return false;
        }
        try {
            String remoteDir = basePath + filePath;
            if (!createRemoteDirectory(ftpClient, remoteDir)) {
                return false;
            }
            // 编码转换(解决中文文件名)
            String remoteFileName = new String(filename.getBytes(LOCAL_CHARSET), SERVER_CHARSET);
            boolean success = ftpClient.storeFile(remoteFileName, input);
            if (success) {
                logger.info("文件上传成功:" + remoteDir + "/" + remoteFileName);
            } else {
                logger.error("文件上传失败,响应码:" + ftpClient.getReplyCode());
            }
            return success;
        } catch (IOException e) {
            logger.error("上传文件异常:" + e.getMessage(), e);
            return false;
        } finally {
            disconnectQuietly(ftpClient);
            try {
                input.close();
            } catch (IOException e) {
                logger.warn("关闭输入流失败:" + e.getMessage());
            }
        }
    }
}
    /**
     * 上传文件(适配麒麟系统,供复用)
     *
     * @param basePath 远程基础路径(如/iot)
     * @param filePath 远程子路径(如data/logs)
     * @param filename 文件名
     * @param input    输入流
     * @return 是否成功
     */
    public boolean uploadFileKylin(String basePath, String filePath, String filename, InputStream input) {
        FTPClient ftpClient = getFTPClient();
        if (ftpClient == null) {
            logger.error("获取FTPClient实例失败,无法执行上传");
            return false;
        }
        try {
            // 麒麟系统FTP服务器必需的设置
            ftpClient.enterLocalPassiveMode(); // 启用被动模式(类Unix系统推荐)
            ftpClient.setFileType(FTP.BINARY_FILE_TYPE); // 二进制传输避免文件损坏
            ftpClient.setControlEncoding("UTF-8"); // 控制连接编码
            // 路径规范化处理(适配麒麟系统Unix路径格式)
            String normalizedBase = normalizePath(basePath);
            String normalizedFilePath = normalizePath(filePath);
            String remoteDir = normalizedBase + normalizedFilePath;
            logger.info("麒麟系统FTP目标目录:" + remoteDir);
            // 创建目录(修正参数顺序,使用规范化路径)
            if (!createKylinFtpDirectory(ftpClient, normalizedBase, normalizedFilePath)) {
                logger.error("目录创建失败,终止上传:" + remoteDir);
                return false;
            }
            // 切换到目标目录(增加验证步骤)
            if (!ftpClient.changeWorkingDirectory(remoteDir)) {
                logger.error("切换到目录[" + remoteDir + "]失败,响应码: {" + ftpClient.getReplyCode() + "}");
                return false;
            }
            // 编码转换(优化中文文件名处理,适配麒麟系统编码)
            String remoteFileName = new String(filename.getBytes(LOCAL_CHARSET), SERVER_CHARSET);
            // 执行上传
            boolean success = ftpClient.storeFile(remoteFileName, input);
            if (success) {
                logger.info("文件上传成功:{" + remoteDir + "/" + filename + "}");
            } else {
                logger.error("文件上传失败,路径:{" + remoteDir + "/" + filename + "},响应码:{" + ftpClient.getReplyCode() + "},响应信息:{" +
                        ftpClient.getReplyString() + "}");
            }
            return success;
        } catch (IOException e) {
            logger.error("上传文件异常:{" + e.getMessage(), e);
            return false;
        } finally {
            disconnectQuietly(ftpClient);
            try {
                input.close();
            } catch (IOException e) {
                logger.warn("关闭输入流失败:{}" + e.getMessage());
            }
        }
    }
    /**
     * 路径规范化处理(适配麒麟系统)
     * 处理重复斜杠、首尾斜杠问题,统一Unix风格路径
     */
    private String normalizePath(String path) {
        if (path == null || path.trim().isEmpty()) {
            return "";
        }
        // 替换多个斜杠为单个,移除尾部斜杠(除根目录外)
        String normalized = path.trim().replaceAll("//+", "/");
        if (normalized.length() > 1 && normalized.endsWith("/")) {
            normalized = normalized.substring(0, normalized.length() - 1);
        }
        return normalized;
    }
    /**
     * 创建文件夹(适配麒麟系统,解决编码/权限/路径问题)
     */
    public boolean createFolder(String pathname, String folderName) {
        FTPClient ftpClient = getFTPClient();
        if (ftpClient == null) {
            return false;
        }
        try {
            // 1. 统一路径格式(Linux兼容)
            String remoteFolder = (pathname + "/" + folderName)
                    .replace("\\", "/")          // 替换Windows反斜杠
                    .replaceAll("/+", "/")        // 合并连续斜杠
                    .replaceAll("^/", "");        // 去除开头的斜杠(避免绝对路径问题)
            // 2. 设置UTF-8编码(解决中文路径问题)
            ftpClient.setControlEncoding("UTF-8");
            remoteFolder = new String(remoteFolder.getBytes("UTF-8"), "ISO-8859-1"); // FTP协议默认编码
            // 3. 检查目录是否存在(幂等性)
            if (ftpClient.changeWorkingDirectory(remoteFolder)) {
                logger.info("文件夹已存在:" + remoteFolder);
                return true;
            }
            // 4. 尝试创建目录(被动模式适配)
            ftpClient.enterLocalPassiveMode(); // 麒麟系统建议用被动模式
            boolean success = ftpClient.makeDirectory(remoteFolder);
            // 5. 记录详细错误信息
            if (!success) {
                int replyCode = ftpClient.getReplyCode();
                String replyMsg = ftpClient.getReplyString();
                logger.error("创建文件夹失败,路径:" + remoteFolder +
                        ",响应码:" + replyCode +
                        ",响应信息:" + replyMsg);
                // 特殊处理550错误(权限/SELinux问题)
                if (replyCode == 550) {
                    logger.error("可能原因:权限不足/SELinux限制/路径不存在");
                }
            } else {
                logger.info("创建文件夹成功:" + remoteFolder);
            }
            return success;
        } catch (IOException e) {
            logger.error("创建文件夹异常:" + e.getMessage(), e);
            return false;
        } finally {
            disconnectQuietly(ftpClient);
        }
    }
    // ====================== 工具方法:字节流转换(保持兼容) ======================
    public InputStream byte2Input(byte[] buf) {
        return new ByteArrayInputStream(Objects.requireNonNull(buf));
    }
    public byte[] input2byte(InputStream inStream) throws IOException {
        try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
            byte[] buffer = new byte[4096];
            int len;
            while ((len = inStream.read(buffer)) != -1) {
                bos.write(buffer, 0, len);
            }
            return bos.toByteArray();
        }
    }
    public void byte2File(byte[] buf, String filePath, String fileName) {
        File dir = new File(filePath);
        if (!dir.exists() && !dir.mkdirs()) {
            logger.error("创建本地目录失败:" + filePath);
            return;
        }
        try (BufferedOutputStream bos = new BufferedOutputStream(
                new FileOutputStream(new File(dir, fileName)))) {
            bos.write(buf);
        } catch (IOException e) {
            logger.error("字节数组写入文件失败:" + fileName, e);
        }
    }
}
lxzn-module-system/lxzn-system-start/src/main/resources/application-dev.yml
@@ -320,4 +320,6 @@
  ftpPassword: lx@2024
  address: /iot/
#数据库 MySql | SqlServer
databaseType: MySql
databaseType: MySql
#操作系统 kylin | windows
operatingSystem: kylin
lxzn-module-system/lxzn-system-start/src/main/resources/application-prod.yml
@@ -320,4 +320,6 @@
  ftpPassword: lx@2024
  address: /iot/
#数据库 MySql | SqlServer
databaseType: MySql
databaseType: MySql
#操作系统 kylin | windows
operatingSystem: kylin