diff --git a/pom.xml b/pom.xml
index eb9fc84..796092e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -43,6 +43,7 @@
1.33
1.12.400
+
2.0.23
3.1.687
@@ -343,7 +344,6 @@
-
diff --git a/ruoyi-common/pom.xml b/ruoyi-common/pom.xml
index 013fb23..837132c 100644
--- a/ruoyi-common/pom.xml
+++ b/ruoyi-common/pom.xml
@@ -167,7 +167,12 @@
org.bouncycastle
bcprov-jdk15to18
-
+
+
+ com.itextpdf
+ itextpdf
+ 5.5.13
+
org.lionsoul
@@ -240,7 +245,16 @@
commons-io
2.15.0
-
+
+ org.apache.pdfbox
+ pdfbox
+
+
+
+ commons-net
+ commons-net
+ 3.6
+
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java
index 68e81cc..9fcdde1 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java
@@ -72,5 +72,21 @@ public interface Constants {
*/
String TOKEN = "token";
+
+ /** 图纸存放路径 */
+ public static final String BASIC_URL = "E:/电子档案/1.标准图纸存放文件夹";
+ public static final String BASIC_URL_BZ = "/1.1标准图纸";
+ public static final String BASIC_URL_FB = "/1.3非标图纸/";
+ public static final String XCL_URL = "/1.1.1 XCL-销齿链标准图纸/";
+ public static final String ELS_URL = "/1.1.2 ELS-行程检测装置标准图纸/";
+ public static final String EDS_URL = "/1.1.3 EDS-行程检测装置(带显示灯)标准图纸/";
+ public static final String WB_URL = "/1.1.4 WB-舞台设备标准图纸/";
+ public static final String QD_URL = "/1.1.5 QD-销齿链驱动单元标准图纸/";
+ public static final String ZHD_URL = "/1.1.6 ZHD-中置换电标准图纸/";
+ public static final String ETK_URL = "/1.1.7 ETK-K系列减速器标准图纸/";
+ public static final String ETT_URL = "/1.1.8 ETT-T系列换向器标准图纸/";
+ public static final String ETP_URL = "/1.1.9 ETP-P系列减速器标准图纸/";
+ public static final String ETH_URL = "/1.1.10 ETH-H系列减速器标准图纸/";
+
}
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/FTPDownload.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/FTPDownload.java
new file mode 100644
index 0000000..9020311
--- /dev/null
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/FTPDownload.java
@@ -0,0 +1,104 @@
+package com.ruoyi.common.utils;
+
+import org.apache.commons.net.ftp.FTPClient;
+import org.apache.commons.net.ftp.FTPFile;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+public class FTPDownload {
+
+ private FTPClient ftpClient;
+
+ public FTPDownload() {
+ ftpClient = new FTPClient();
+ }
+
+ /**
+ * 连接到FTP服务器
+ */
+ public void connect(String ftpHost, String ftpUserName, String ftpPassword, int ftpPort) {
+ try {
+ ftpClient.connect(ftpHost, ftpPort);
+ ftpClient.login(ftpUserName, ftpPassword);
+ ftpClient.enterLocalPassiveMode(); // 设置被动模式
+ System.out.println("FTP连接成功!");
+ } catch (IOException e) {
+ System.out.println("FTP连接失败!");
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * 列出远程目录中的文件
+ * @param remoteDir 远程目录路径
+ * @return FTPFile列表
+ * @throws IOException
+ */
+ public FTPFile[] listFiles(String remoteDir) throws IOException {
+ return ftpClient.listFiles(remoteDir);
+ }
+
+ /**
+ * 下载文件
+ * @param remoteFile 远程文件路径
+ * @param localFile 本地保存路径
+ * @throws IOException
+ */
+ public void downloadFile(String remoteFile, String localFile) throws IOException {
+ try (OutputStream os = new FileOutputStream(localFile)) {
+ ftpClient.retrieveFile(remoteFile, os);
+ System.out.println("文件下载成功: " + remoteFile);
+ } catch (IOException e) {
+ System.out.println("文件下载失败: " + remoteFile);
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * 下载整个目录中的所有文件
+ * @param remoteDir 远程目录路径
+ * @param localDir 本地保存目录
+ */
+ public void downloadFilesInDirectory(String remoteDir, String localDir) {
+ try {
+ FTPFile[] files = listFiles(remoteDir);
+ for (FTPFile file : files) {
+ String remoteFilePath = remoteDir + "/" + file.getName();
+ String localFilePath = localDir + "/" + file.getName();
+
+ if (file.isFile()) {
+ downloadFile(remoteFilePath, localFilePath);
+ } else if (file.isDirectory()) {
+ // 递归下载文件夹
+ File localFolder = new File(localFilePath);
+ if (!localFolder.exists()) {
+ localFolder.mkdirs(); // 创建本地文件夹
+ }
+ downloadFilesInDirectory(remoteFilePath, localFilePath); // 递归下载子文件夹
+ }
+ }
+ } catch (IOException e) {
+ System.out.println("列出文件失败: " + remoteDir);
+ e.printStackTrace();
+ }
+ }
+
+ public static void main(String[] args) {
+ FTPDownload ftpDownload = new FTPDownload();
+ String ftpHost = "192.168.5.18"; // FTP服务器IP
+ String ftpUserName = "admin"; // FTP用户名
+ String ftpPassword = "hbyt2025"; // FTP密码
+ int ftpPort = 21; // FTP端口,默认21
+
+ String remoteDir = "/FB-25-039-FS-01/FS25040.03.0-PDF"; // 远程目录
+ String localDir = "F:/FB-25-039-FS-01"; // 本地保存目录
+
+ // 连接到FTP服务器
+ ftpDownload.connect(ftpHost, ftpUserName, ftpPassword, ftpPort);
+
+ // 下载文件
+ ftpDownload.downloadFilesInDirectory(remoteDir, localDir);
+ }
+}
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/FtpUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/FtpUtil.java
new file mode 100644
index 0000000..f6ccede
--- /dev/null
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/FtpUtil.java
@@ -0,0 +1,722 @@
+package com.ruoyi.common.utils;
+
+import com.ruoyi.common.core.domain.R;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.net.ftp.*;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.X509TrustManager;
+import java.io.*;
+import java.net.SocketException;
+import java.nio.charset.StandardCharsets;
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+
+@Slf4j
+public class FtpUtil {
+ private FTPClient ftpClient;
+ /**
+ * 本地字符编码
+ */
+ private static String LOCAL_CHARSET = "UTF-8";
+
+ // FTP协议里面,规定文件名编码为iso-8859-1
+ private static final String SERVER_CHARSET = "ISO-8859-1";
+ //ftp的端口,主机,用户名和密码
+ private static final int port = 8022;
+ private static final String host = "192.168.5.18";
+ private static final String username = "admin";
+ private static final String password = "hbyt2025";
+
+ public void FTPDownload() {
+ ftpClient = new FTPClient();
+ }
+
+
+ private static String encodePath(String path) throws UnsupportedEncodingException {
+ return new String(path.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1);
+ }
+
+ /**
+ * 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 host, int port, String username, String password,
+ String basePath, String filePath, String filename, InputStream input) {
+ boolean result = false;
+ FTPClient ftpClient = new FTPClient();
+
+ try {
+ // 1. 连接并登录
+ ftpClient.connect(host, port);
+ ftpClient.login(username, password);
+
+ ftpClient.enterLocalPassiveMode(); // 设置被动模式
+ ftpClient.setFileType(FTP.BINARY_FILE_TYPE); // 设置为二进制传输模式
+ ftpClient.sendCommand("OPTS UTF8", "ON"); // 请求服务器启用 UTF-8
+ ftpClient.setControlEncoding("UTF-8"); // 控制编码也设为 UTF-8
+
+ int reply = ftpClient.getReplyCode();
+ if (!FTPReply.isPositiveCompletion(reply)) {
+ ftpClient.disconnect();
+ System.err.println("FTP 登录失败,响应码:" + reply);
+ return false;
+ }
+// ftpClient.changeWorkingDirectory(basePath);
+
+ // 2. 尝试切换到目标目录,不存在就创建
+ if (!ftpClient.changeWorkingDirectory(filePath)) {
+ String[] dirs = filePath.split("/");
+ for (String dir : dirs) {
+ if (dir == null || dir.trim().isEmpty()) continue;
+ String encodedDir = new String(dir.getBytes(LOCAL_CHARSET), SERVER_CHARSET);
+ if (!ftpClient.changeWorkingDirectory(encodedDir)) {
+ if (!ftpClient.makeDirectory(encodedDir)) {
+ System.err.println("创建目录失败:" + dir);
+ return false;
+ } else {
+ ftpClient.changeWorkingDirectory(encodedDir);
+ }
+ }
+ }
+ }
+
+ // 3. 编码与传输设置
+ if (FTPReply.isPositiveCompletion(ftpClient.sendCommand("OPTS UTF8", "ON"))) {
+ ftpClient.setControlEncoding("UTF-8");
+ } else {
+ ftpClient.setControlEncoding("GBK");
+ }
+
+ // 4. 文件名编码处理
+ filename = new String(filename.getBytes(LOCAL_CHARSET), SERVER_CHARSET);
+
+ // 5. 上传文件
+ boolean stored = ftpClient.storeFile(filename, input);
+ if (!stored) {
+ System.err.println("上传失败,文件名:" + filename);
+ return false;
+ }
+
+ result = true;
+ } catch (IOException e) {
+ System.err.println("上传异常:" + e.getMessage());
+ e.printStackTrace();
+ } finally {
+ // 6. 资源清理
+ try {
+ if (input != null) input.close();
+ if (ftpClient.isConnected()) {
+ ftpClient.logout();
+ ftpClient.disconnect();
+ }
+ } catch (IOException ex) {
+ ex.printStackTrace();
+ }
+ }
+ return result;
+ }
+ /**
+ * 获取FTPClient对象
+ *
+ * @param ftpHost FTP主机服务器
+ * @param ftpPassword FTP 登录密码
+ * @param ftpUserName FTP登录用户名
+ * @param ftpPort FTP端口 默认为21
+ * @return
+ */
+ public static FTPSClient getFTPClient1(String ftpHost, int ftpPort, String ftpUserName, String ftpPassword) {
+ FTPSClient ftpClient = null;
+ try {
+ //创建SSL上下文
+ SSLContext sslContext = SSLContext.getInstance("TLS");
+ //自定义证书,忽略已过期证书
+ X509TrustManager trustManager = new X509TrustManager() {
+ @Override
+ public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {}
+ @Override
+ public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {}
+ @Override
+ public X509Certificate[] getAcceptedIssuers() {return new X509Certificate[0];}
+ };
+ //初始化
+ sslContext.init(null, new X509TrustManager[] { trustManager }, null);
+ ftpClient = new FTPSClient(sslContext);
+ ftpClient.connect(ftpHost, ftpPort);// 连接FTP服务器
+ Boolean login = ftpClient.login(ftpUserName, ftpPassword);// 登陆FTP服务器
+ log.info("login: "+login);
+ if (!FTPReply.isPositiveCompletion(ftpClient.getReplyCode())) {
+ log.info("未连接到FTP,用户名或密码错误。");
+ ftpClient.disconnect();
+ } else {
+ log.info("FTP连接成功。");
+ }
+ } catch (SocketException e) {
+ e.printStackTrace();
+ log.info("FTP的IP地址可能错误,请正确配置。");
+ } catch (IOException e) {
+ e.printStackTrace();
+ log.info("FTP的端口错误,请正确配置。");
+ } catch (NoSuchAlgorithmException e) {
+ e.printStackTrace();
+ } catch (KeyManagementException e) {
+ e.printStackTrace();
+ }
+ return ftpClient;
+ }
+
+
+
+
+
+ private static final String encoding = System.getProperty("file.encoding");
+
+ /**
+ * 上传文件到FTP,自动创建目录
+ *
+ * @param ftpHost FTP服务器地址
+ * @param ftpUserName 用户名
+ * @param ftpPassword 密码
+ * @param ftpPort 端口
+ * @param remoteDirPath 要上传到的远程目录(如:/upload/2025/图纸)
+ * @param fileName 要保存的文件名
+ * @param inputStream 文件流
+ * @return 是否上传成功
+ */
+ public static boolean uploadWithAutoMkdir(String ftpHost, String ftpUserName, String ftpPassword, int ftpPort,
+ String remoteDirPath, String fileName, InputStream inputStream) {
+ FTPClient ftpClient = null;
+ try {
+ ftpClient = getFTPClient(ftpHost, ftpUserName, ftpPassword, ftpPort);
+ ftpClient.setControlEncoding("UTF-8");
+ ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
+ ftpClient.enterLocalPassiveMode();
+
+ String rootPath = "/电子档案/1.标准图纸存放文件夹";
+
+ // 改为逐级创建根目录
+ String[] rootPaths = rootPath.split("/");
+ String currentPath = "";
+ for (String dir : rootPaths) {
+ if (dir == null || dir.trim().isEmpty()) continue;
+ currentPath += "/" + dir;
+ if (!ftpClient.changeWorkingDirectory(currentPath)) {
+ if (!ftpClient.makeDirectory(currentPath)) {
+ log.error("无法创建根目录: {}", currentPath);
+ return false;
+ }
+ ftpClient.changeWorkingDirectory(currentPath);
+ }
+ }
+
+ // 继续创建 remoteDirPath(在rootPath之后)
+ String[] paths = remoteDirPath.split("/");
+ for (String path : paths) {
+ if (path == null || path.trim().isEmpty()) continue;
+ currentPath += "/" + path;
+ if (!ftpClient.changeWorkingDirectory(currentPath)) {
+ if (!ftpClient.makeDirectory(currentPath)) {
+ log.error("无法创建目录: {}", currentPath);
+ return false;
+ }
+ ftpClient.changeWorkingDirectory(currentPath);
+ }
+ }
+
+ // ✅ 上传文件
+ boolean uploaded = ftpClient.storeFile(fileName, inputStream);
+ inputStream.close();
+ ftpClient.logout();
+ return uploaded;
+ } catch (Exception e) {
+ log.error("FTP上传异常", e);
+ return false;
+ } finally {
+ if (ftpClient != null && ftpClient.isConnected()) {
+ try {
+ ftpClient.disconnect();
+ } catch (IOException e) {
+ log.error("FTP关闭异常", e);
+ }
+ }
+ }
+ }
+
+
+ public static boolean uploadFile(String url, int port, String username,
+ String password, String path, String filename, InputStream input) {
+ boolean result = false;
+ FTPClient ftpClient = null;
+ try {
+ int reply;
+ // 如果采用默认端口,可以使用ftp.connect(url)的方式直接连接FTP服务器
+ ftpClient.connect(url);
+ ftpClient.connect(url, port);// 连接FTP服务器
+ // 登录
+ ftpClient.login(username, password);
+ ftpClient.setControlEncoding(encoding);
+ // 检验是否连接成功
+ reply = ftpClient.getReplyCode();
+ if (!FTPReply.isPositiveCompletion(reply)) {
+ System.out.println("连接失败");
+ ftpClient.disconnect();
+ return result;
+ }
+
+ // 转移工作目录至指定目录下
+ boolean change = ftpClient.changeWorkingDirectory(path);
+ ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
+ if (change) {
+ result = ftpClient.storeFile(new String(filename.getBytes(encoding), "iso-8859-1"), input);
+ if (result) {
+ System.out.println("上传成功!");
+ }
+ }
+ input.close();
+ ftpClient.logout();
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ if (ftpClient.isConnected()) {
+ try {
+ ftpClient.disconnect();
+ } catch (IOException ioe) {
+ }
+ }
+ }
+ return result;
+ }
+
+
+ /**
+ * 获取FTPClient对象
+ *
+ * @param ftpHost FTP主机服务器
+ * @param ftpPassword FTP 登录密码
+ * @param ftpUserName FTP登录用户名
+ * @param ftpPort FTP端口 默认为21
+ * @return
+ */
+ public static FTPClient getFTPClient(String ftpHost, String ftpUserName,
+ String ftpPassword, int ftpPort) {
+ FTPClient ftpClient = new FTPClient();
+ try {
+ ftpClient = new FTPClient();
+ ftpClient.connect(ftpHost, ftpPort);// 连接FTP服务器
+ ftpClient.login(ftpUserName, ftpPassword);// 登录FTP服务器
+ if (!FTPReply.isPositiveCompletion(ftpClient.getReplyCode())) {
+ System.out.println("未连接到FTP,用户名或密码错误。");
+ ftpClient.disconnect();
+ } else {
+ System.out.println("FTP连接成功。");
+ }
+ } catch (SocketException e) {
+ e.printStackTrace();
+ System.out.println("FTP的IP地址可能错误,请正确配置。");
+ } catch (IOException e) {
+ e.printStackTrace();
+ System.out.println("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(ftpHost, ftpUserName, ftpPassword, ftpPort);
+ ftpClient.setControlEncoding("UTF-8"); // 中文支持
+ ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
+ ftpClient.enterLocalPassiveMode();
+ ftpClient.changeWorkingDirectory(ftpPath);
+
+ File localFile = new File(localPath + File.separatorChar + fileName);
+ OutputStream os = new FileOutputStream(localFile);
+ ftpClient.retrieveFile(fileName, os);
+ os.close();
+ ftpClient.logout();
+
+ } catch (FileNotFoundException e) {
+ System.out.println("没有找到" + ftpPath + "文件");
+ e.printStackTrace();
+ } catch (SocketException e) {
+ System.out.println("连接FTP失败.");
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ System.out.println("文件读取错误。");
+ e.printStackTrace();
+ }
+
+ }
+
+
+ /**
+ * 从输入流中获取字节数组
+ *
+ * @param inputStream
+ * @return
+ * @throws IOException
+ */
+ public static byte[] readInputStream(InputStream inputStream) throws IOException {
+ byte[] buffer = new byte[1024];
+ int len = 0;
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ while ((len = inputStream.read(buffer)) != -1) {
+ bos.write(buffer, 0, len);
+ }
+ bos.close();
+ return bos.toByteArray();
+ }
+
+ /**
+ * Description: 向FTP服务器上传文件
+ *
+ * @param ftpHost FTP服务器hostname
+ * @param ftpUserName 账号
+ * @param ftpPassword 密码
+ * @param ftpPort 端口
+ * @param ftpPath FTP服务器中文件所在路径 格式: ftptest/aa
+ * @param fileName ftp文件名称
+ * @param input 文件流
+ * @return 成功返回true,否则返回false
+ */
+ public static boolean uploadFile(String ftpHost, String ftpUserName, String ftpPassword, int ftpPort, String ftpPath, String fileName, InputStream input, String filePath, long filesize) {
+ boolean success = false;
+ FTPClient ftpClient = null;
+ String[] ftpPathDir = ftpPath.split("/");//ftp目录
+
+ String[] dir = filePath.split("/");//创建多级目录
+ try {
+ int reply;
+ ftpClient = getFTPClient(ftpHost, ftpUserName, ftpPassword, ftpPort);
+ reply = ftpClient.getReplyCode();
+
+ for (int i = 1; i < ftpPathDir.length; i++) {
+ ftpClient.makeDirectory(new String(ftpPathDir[i].getBytes("GBK"), "iso-8859-1"));
+ if (!FTPReply.isPositiveCompletion(reply)) {
+ ftpClient.disconnect();
+ return success;
+ }
+ ftpClient.setControlEncoding("GBK"); // 中文支持
+ ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
+ ftpClient.enterLocalPassiveMode();
+ //取消服务器获取自身Ip地址和提交的host进行匹配 当不一致时会抛出异常
+ ftpClient.setRemoteVerificationEnabled(false);
+ ftpClient.changeWorkingDirectory(new String(ftpPathDir[i].getBytes("GBK"), "iso-8859-1"));
+ }
+
+ for (int i = 0; i < dir.length; i++) {
+ if (!dir[i].equals("")) {
+ ftpClient.makeDirectory(new String(dir[i].getBytes("GBK"), "iso-8859-1"));
+ if (!FTPReply.isPositiveCompletion(reply)) {
+ ftpClient.disconnect();
+ return success;
+ }
+ ftpClient.setControlEncoding("GBK"); // 中文支持
+ ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
+ ftpClient.enterLocalPassiveMode();
+ ftpClient.changeWorkingDirectory(new String(dir[i].getBytes("GBK"), "iso-8859-1"));
+ }
+
+ }
+ //默认FTP上传速度过慢,由于默认缓冲区大小1024字节,将缓冲区大小改为10M
+ ftpClient.setBufferSize(1024 * 1024 * 10);
+ ftpClient.storeFile(new String(fileName.getBytes("GBK"), "iso-8859-1"), input);
+ input.close();
+ ftpClient.logout();
+ success = true;
+ } catch (Exception e) {
+ e.printStackTrace();
+ return success;
+ } finally {
+ if (ftpClient.isConnected()) {
+ try {
+ ftpClient.disconnect();
+ } catch (IOException ioe) {
+ }
+ }
+ }
+ return success;
+ }
+
+ public static void downloadFtpFolder(String ftpHost, String ftpUserName,
+ String ftpPassword, int ftpPort, String ftpPath, String localPath) {
+
+ FTPClient ftpClient = null;
+ try {
+ ftpClient = getFTPClient(ftpHost, ftpUserName, ftpPassword, ftpPort);
+ ftpClient.setControlEncoding("UTF-8"); // 中文支持
+ ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
+ ftpClient.enterLocalPassiveMode(); // 设置被动模式
+
+ // 确保进入指定的目标目录(例如 FS25040.03.0-PDF)
+ ftpClient.changeWorkingDirectory(ftpPath); // 切换到目标文件夹
+
+ // 获取文件夹中的内容
+ FTPFile[] files = ftpClient.listFiles();
+ if (files != null) {
+ for (FTPFile file : files) {
+ String fileName = file.getName();
+ String remoteFilePath = ftpPath + "/" + fileName;
+ File localFile = new File(localPath + "/" + fileName);
+
+ // 如果是文件,则下载
+ if (file.isFile()) {
+ System.out.println("下载文件: " + remoteFilePath);
+ try (OutputStream os = new FileOutputStream(localFile)) {
+ ftpClient.retrieveFile(remoteFilePath, os);
+ } catch (IOException e) {
+ System.out.println("下载失败: " + remoteFilePath);
+ e.printStackTrace();
+ }
+ }
+ // 如果是文件夹,递归调用(但仅当路径是我们需要的文件夹时才递归)
+ else if (file.isDirectory()) {
+ String subFolderPath = ftpPath + "/" + fileName;
+ File subLocalFolder = new File(localPath + "/" + fileName);
+ if (!subLocalFolder.exists()) {
+ subLocalFolder.mkdirs(); // 创建子文件夹
+ }
+
+ // 递归下载子文件夹
+ downloadFtpFolder(ftpHost, ftpUserName, ftpPassword, ftpPort, subFolderPath, subLocalFolder.getAbsolutePath());
+ }
+ }
+ }
+ ftpClient.logout();
+ } catch (IOException e) {
+ System.out.println("FTP 操作失败");
+ e.printStackTrace();
+ } finally {
+ if (ftpClient != null && ftpClient.isConnected()) {
+ try {
+ ftpClient.disconnect();
+ } catch (IOException ioe) {
+ ioe.printStackTrace();
+ }
+ }
+ }
+ }
+
+ /**
+ * 列出远程目录中的文件
+ *
+ * @param remoteDir 远程目录路径
+ * @return FTPFile列表
+ * @throws IOException
+ */
+ public FTPFile[] listFiles(String remoteDir) throws IOException {
+ return ftpClient.listFiles(remoteDir);
+ }
+
+ /**
+ * 下载文件
+ *
+ * @param remoteFile 远程文件路径
+ * @param localFile 本地保存路径
+ * @throws IOException
+ */
+ public void downloadFile(String remoteFile, String localFile) throws IOException {
+ try (OutputStream os = new FileOutputStream(localFile)) {
+ ftpClient.retrieveFile(remoteFile, os);
+ System.out.println("文件下载成功: " + remoteFile);
+ } catch (IOException e) {
+ System.out.println("文件下载失败: " + remoteFile);
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * 下载整个目录中的所有文件
+ *
+ * @param remoteDir 远程目录路径
+ * @param localDir 本地保存目录
+ */
+ public void downloadFilesInDirectory(String remoteDir, String localDir) {
+ try {
+ FTPFile[] files = listFiles(remoteDir);
+ for (FTPFile file : files) {
+ String remoteFilePath = remoteDir + "/" + file.getName();
+ String localFilePath = localDir + "/" + file.getName();
+
+ if (file.isFile()) {
+ downloadFile(remoteFilePath, localFilePath);
+ } else if (file.isDirectory()) {
+ // 递归下载文件夹
+ File localFolder = new File(localFilePath);
+ if (!localFolder.exists()) {
+ localFolder.mkdirs(); // 创建本地文件夹
+ }
+ downloadFilesInDirectory(remoteFilePath, localFilePath); // 递归下载子文件夹
+ }
+ }
+ } catch (IOException e) {
+ System.out.println("列出文件失败: " + remoteDir);
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * 下载FTP指定目录中的所有文件到本地
+ *
+ * @param ftpHost FTP服务器IP
+ * @param ftpUserName FTP用户名
+ * @param ftpPassword FTP密码
+ * @param ftpPort FTP端口
+ * @param remoteDir 远程目录路径
+ * @param localDir 本地保存目录
+ * @return 下载结果
+ */
+ public static R downloadFtpDirectoryFiles(String ftpHost, String ftpUserName,
+ String ftpPassword, int ftpPort,
+ String remoteDir, String localDir) {
+ FTPClient ftpClient = null;
+ try {
+ // 1. 连接FTP服务器
+ ftpClient = getFTPClient(ftpHost, ftpUserName, ftpPassword, ftpPort);
+ if (ftpClient == null || !ftpClient.isConnected()) {
+ return R.fail("FTP连接失败");
+ }
+
+ // 2. 设置FTP参数
+ ftpClient.setControlEncoding("UTF-8");
+ ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
+ ftpClient.enterLocalPassiveMode();
+
+ // 3. 切换到远程目录
+ if (!ftpClient.changeWorkingDirectory(remoteDir)) {
+ return R.fail("远程目录不存在: " + remoteDir);
+ }
+
+ // 4. 创建本地目录
+ File localDirectory = new File(localDir);
+ if (!localDirectory.exists()) {
+ if (!localDirectory.mkdirs()) {
+ return R.fail("无法创建本地目录: " + localDir);
+ }
+ }
+
+ // 5. 获取远程目录文件列表
+ FTPFile[] files = ftpClient.listFiles();
+ if (files == null || files.length == 0) {
+ return R.ok("远程目录为空,无需下载");
+ }
+
+ // 6. 遍历下载文件
+ int successCount = 0;
+ int totalCount = files.length;
+ StringBuilder errorMsg = new StringBuilder();
+
+ for (FTPFile file : files) {
+ String fileName = file.getName();
+
+ // 跳过隐藏文件和目录
+ if (fileName.startsWith(".") || file.isDirectory()) {
+ continue;
+ }
+
+ try {
+ // 构建本地文件路径
+ String localFilePath = localDir + File.separator + fileName;
+ File localFile = new File(localFilePath);
+
+ // 下载文件
+ try (OutputStream os = new FileOutputStream(localFile)) {
+ boolean downloadSuccess = ftpClient.retrieveFile(fileName, os);
+ if (downloadSuccess) {
+ successCount++;
+ log.info("文件下载成功: {}", fileName);
+ } else {
+ errorMsg.append("文件下载失败: ").append(fileName).append("; ");
+ log.error("文件下载失败: {}", fileName);
+ }
+ }
+
+ } catch (Exception e) {
+ errorMsg.append("文件下载异常: ").append(fileName).append(" - ").append(e.getMessage()).append("; ");
+ log.error("文件下载异常: {}", fileName, e);
+ }
+ }
+
+ // 7. 返回结果
+ String resultMsg = String.format("下载完成: 成功%d个文件,共%d个文件", successCount, totalCount);
+ if (errorMsg.length() > 0) {
+ resultMsg += ",错误信息: " + errorMsg.toString();
+ }
+
+ return R.ok(resultMsg);
+
+ } catch (Exception e) {
+ log.error("FTP下载过程中发生异常", e);
+ return R.fail("下载失败: " + e.getMessage());
+ } finally {
+ // 8. 关闭FTP连接
+ if (ftpClient != null && ftpClient.isConnected()) {
+ try {
+ ftpClient.logout();
+ ftpClient.disconnect();
+ } catch (IOException e) {
+ log.error("关闭FTP连接失败", e);
+ }
+ }
+ }
+ }
+
+ /**
+ * 主方法 - 用于测试FTP目录文件下载功能
+ */
+ public static void main(String[] args) {
+ // FTP服务器配置
+ String ftpHost = "192.168.5.18";
+ String ftpUserName = "admin";
+ String ftpPassword = "hbyt2025";
+ int ftpPort = 21;
+
+ // 远程目录和本地目录配置
+ String remoteDir = "/FB-25-039-FS-01/40S-R-2720-T(FS039-25)-PDF"; // 远程目录路径
+ String localDir = "F:/DownloadedFiles"; // 本地保存目录
+
+ System.out.println("开始下载FTP文件...");
+ System.out.println("FTP服务器: " + ftpHost);
+ System.out.println("远程目录: " + remoteDir);
+ System.out.println("本地目录: " + localDir);
+ System.out.println("=====================================");
+
+ // 调用下载方法
+ R result = downloadFtpDirectoryFiles(ftpHost, ftpUserName, ftpPassword, ftpPort, remoteDir, localDir);
+
+ // 输出结果
+ if (result.getMsg().equals("1")) {
+ System.out.println("✅ 下载成功: " + result.getData());
+ } else {
+ System.out.println("❌ 下载失败: " + result.getMsg());
+ }
+
+ System.out.println("=====================================");
+ System.out.println("下载完成!");
+ }
+
+
+}
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/HttpUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/HttpUtils.java
new file mode 100644
index 0000000..5733c61
--- /dev/null
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/HttpUtils.java
@@ -0,0 +1,254 @@
+package com.ruoyi.common.utils;
+
+import com.ruoyi.common.constant.Constants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.net.ssl.*;
+import java.io.*;
+import java.net.ConnectException;
+import java.net.SocketTimeoutException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.security.cert.X509Certificate;
+
+/**
+ * 通用http发送方法
+ *
+ * @author ruoyi
+ */
+public class HttpUtils
+{
+ private static final Logger log = LoggerFactory.getLogger(HttpUtils.class);
+
+ /**
+ * 向指定 URL 发送GET方法的请求
+ *
+ * @param url 发送请求的 URL
+ * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
+ * @return 所代表远程资源的响应结果
+ */
+ public static String sendGet(String url, String param)
+ {
+ return sendGet(url, param, Constants.UTF8);
+ }
+
+ /**
+ * 向指定 URL 发送GET方法的请求
+ *
+ * @param url 发送请求的 URL
+ * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
+ * @param contentType 编码类型
+ * @return 所代表远程资源的响应结果
+ */
+ public static String sendGet(String url, String param, String contentType)
+ {
+ StringBuilder result = new StringBuilder();
+ BufferedReader in = null;
+ try
+ {
+ String urlNameString = url + "?" + param;
+ log.info("sendGet - {}", urlNameString);
+ URL realUrl = new URL(urlNameString);
+ URLConnection connection = realUrl.openConnection();
+ connection.setRequestProperty("accept", "*/*");
+ connection.setRequestProperty("connection", "Keep-Alive");
+ connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
+ connection.connect();
+ in = new BufferedReader(new InputStreamReader(connection.getInputStream(), contentType));
+ String line;
+ while ((line = in.readLine()) != null)
+ {
+ result.append(line);
+ }
+ log.info("recv - {}", result);
+ }
+ catch (ConnectException e)
+ {
+ log.error("调用HttpUtils.sendGet ConnectException, url=" + url + ",param=" + param, e);
+ }
+ catch (SocketTimeoutException e)
+ {
+ log.error("调用HttpUtils.sendGet SocketTimeoutException, url=" + url + ",param=" + param, e);
+ }
+ catch (IOException e)
+ {
+ log.error("调用HttpUtils.sendGet IOException, url=" + url + ",param=" + param, e);
+ }
+ catch (Exception e)
+ {
+ log.error("调用HttpsUtil.sendGet Exception, url=" + url + ",param=" + param, e);
+ }
+ finally
+ {
+ try
+ {
+ if (in != null)
+ {
+ in.close();
+ }
+ }
+ catch (Exception ex)
+ {
+ log.error("调用in.close Exception, url=" + url + ",param=" + param, ex);
+ }
+ }
+ return result.toString();
+ }
+
+ /**
+ * 向指定 URL 发送POST方法的请求
+ *
+ * @param url 发送请求的 URL
+ * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
+ * @return 所代表远程资源的响应结果
+ */
+ public static String sendPost(String url, String param)
+ {
+ PrintWriter out = null;
+ BufferedReader in = null;
+ StringBuilder result = new StringBuilder();
+ try
+ {
+ String urlNameString = url;
+ log.info("sendPost - {}", urlNameString);
+ URL realUrl = new URL(urlNameString);
+ URLConnection conn = realUrl.openConnection();
+ conn.setRequestProperty("Accept", "*/*");
+ conn.setRequestProperty("connection", "Keep-Alive");
+ conn.setRequestProperty("Accept-Charset", "UTF-8");
+ conn.setRequestProperty("Accept-Encoding", "UTF-8");
+ conn.setRequestProperty("Content-Type", "application/json");
+ conn.setDoOutput(true);
+ conn.setDoInput(true);
+ out = new PrintWriter(conn.getOutputStream());
+ out.print(param);
+ out.flush();
+ in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));
+ String line;
+ while ((line = in.readLine()) != null)
+ {
+ result.append(line);
+ }
+ log.info("recv - {}", result);
+ }
+ catch (ConnectException e)
+ {
+ log.error("调用HttpUtils.sendPost ConnectException, url=" + url + ",param=" + param, e);
+ }
+ catch (SocketTimeoutException e)
+ {
+ log.error("调用HttpUtils.sendPost SocketTimeoutException, url=" + url + ",param=" + param, e);
+ }
+ catch (IOException e)
+ {
+ log.error("调用HttpUtils.sendPost IOException, url=" + url + ",param=" + param, e);
+ }
+ catch (Exception e)
+ {
+ log.error("调用HttpsUtil.sendPost Exception, url=" + url + ",param=" + param, e);
+ }
+ finally
+ {
+ try
+ {
+ if (out != null)
+ {
+ out.close();
+ }
+ if (in != null)
+ {
+ in.close();
+ }
+ }
+ catch (IOException ex)
+ {
+ log.error("调用in.close Exception, url=" + url + ",param=" + param, ex);
+ }
+ }
+ return result.toString();
+ }
+
+ public static String sendSSLPost(String url, String param)
+ {
+ StringBuilder result = new StringBuilder();
+ String urlNameString = url + "?" + param;
+ try
+ {
+ log.info("sendSSLPost - {}", urlNameString);
+ SSLContext sc = SSLContext.getInstance("SSL");
+ sc.init(null, new TrustManager[] { new TrustAnyTrustManager() }, new java.security.SecureRandom());
+ URL console = new URL(urlNameString);
+ HttpsURLConnection conn = (HttpsURLConnection) console.openConnection();
+ conn.setRequestProperty("accept", "*/*");
+ conn.setRequestProperty("connection", "Keep-Alive");
+ conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
+ conn.setRequestProperty("Accept-Charset", "UTF-8");
+ conn.setRequestProperty("contentType", "UTF-8");
+ conn.setDoOutput(true);
+ conn.setDoInput(true);
+
+ conn.setSSLSocketFactory(sc.getSocketFactory());
+ conn.setHostnameVerifier(new TrustAnyHostnameVerifier());
+ conn.connect();
+ InputStream is = conn.getInputStream();
+ BufferedReader br = new BufferedReader(new InputStreamReader(is));
+ String ret = "";
+ while ((ret = br.readLine()) != null)
+ {
+ if (ret != null && !ret.trim().equals(""))
+ {
+ result.append(new String(ret.getBytes("ISO-8859-1"), "UTF-8"));
+ }
+ }
+ log.info("recv - {}", result);
+ conn.disconnect();
+ br.close();
+ }
+ catch (ConnectException e)
+ {
+ log.error("调用HttpUtils.sendSSLPost ConnectException, url=" + url + ",param=" + param, e);
+ }
+ catch (SocketTimeoutException e)
+ {
+ log.error("调用HttpUtils.sendSSLPost SocketTimeoutException, url=" + url + ",param=" + param, e);
+ }
+ catch (IOException e)
+ {
+ log.error("调用HttpUtils.sendSSLPost IOException, url=" + url + ",param=" + param, e);
+ }
+ catch (Exception e)
+ {
+ log.error("调用HttpsUtil.sendSSLPost Exception, url=" + url + ",param=" + param, e);
+ }
+ return result.toString();
+ }
+
+ private static class TrustAnyTrustManager implements X509TrustManager
+ {
+ @Override
+ public void checkClientTrusted(X509Certificate[] chain, String authType)
+ {
+ }
+
+ @Override
+ public void checkServerTrusted(X509Certificate[] chain, String authType)
+ {
+ }
+
+ @Override
+ public X509Certificate[] getAcceptedIssuers()
+ {
+ return new X509Certificate[] {};
+ }
+ }
+
+ private static class TrustAnyHostnameVerifier implements HostnameVerifier
+ {
+ @Override
+ public boolean verify(String hostname, SSLSession session)
+ {
+ return true;
+ }
+ }
+}
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/WxRobotUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/WxRobotUtil.java
index a9a0035..a8de132 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/WxRobotUtil.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/WxRobotUtil.java
@@ -7,7 +7,11 @@ import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
+import java.io.BufferedReader;
import java.io.File;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.URL;
import java.util.*;
@Slf4j
@@ -62,7 +66,7 @@ public class WxRobotUtil {
HashMap paramMap = new HashMap<>();
HashMap textMap = new HashMap<>();
textMap.put("content", msg);
-
+
// 添加@所有人的配置
if (mentionAll) {
textMap.put("mentioned_list", Collections.singletonList("@all"));
@@ -70,7 +74,7 @@ public class WxRobotUtil {
paramMap.put("msgtype", "text");
paramMap.put("text", textMap);
-
+
String sendUrl = SEND_MESSAGE_URL + "?key=" + robotId;
ResponseEntity