javascript
复制代码
package com.skybird.iot.addons.flow.flowCreator.web.nodes.fileManage.backend;
import cn.hutool.core.util.StrUtil;
import com.skybird.commons.mongo.DocuLib;
import com.skybird.iot.addons.flow.flowCreator.backend.base.service.FlowNodeRunBaseService;
import com.skybird.iot.addons.flow.flowCreator.backend.dto.FlowRunDataDto;
import com.skybird.iot.addons.flow.flowCreator.backend.util.FlowFtpUtils;
import com.skybird.iot.addons.flow.flowCreator.backend.util.FlowProtocolAccessLogUtil;
import com.skybird.iot.addons.flow.flowCreator.backend.util.FlowSharedDirectoryUtils;
import com.skybird.iot.addons.flow.flowCreator.web.nodes.fileManage.backend.service.FlowOssService;
import jakarta.annotation.Resource;
import org.bson.Document;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class FlowSharedDirectoryDownloadService extends FlowNodeRunBaseService {
private static final Logger LOGGER =
LoggerFactory.getLogger(FlowSharedDirectoryDownloadService.class.getName());
@Resource
FlowFolderJudgmentReadService flowFolderJudgmentReadService;
@Override
public String[] getComponentKey() {
return new String[]{"shared_directory_download"};
}
@Override
public void run(FlowRunDataDto flowRunDataDto) throws Exception {
Document nodeConfig = flowRunDataDto.getNodeConfig();
String ipRow = String.valueOf(flowRunDataDto.getFieldValue(DocuLib.getDocu(nodeConfig, "ip")));
String username = String.valueOf(flowRunDataDto.getFieldValue(DocuLib.getDocu(nodeConfig, "username")));
String password = String.valueOf(flowRunDataDto.getFieldValue(DocuLib.getDocu(nodeConfig, "password")));
String fileName = String.valueOf(flowRunDataDto.getFieldValue(DocuLib.getDocu(nodeConfig, "fileName")));
String localFilePath = String.valueOf(flowRunDataDto.getFieldValue(DocuLib.getDocu(nodeConfig, "localFilePath")));
String directoryPath = String.valueOf(flowRunDataDto.getFieldValue(DocuLib.getDocu(nodeConfig, "directoryPath")));
String fileExtension = String.valueOf(flowRunDataDto.getFieldValue(DocuLib.getDocu(nodeConfig, "fileExtension")));
String excludeFileName = String.valueOf(flowRunDataDto.getFieldValue(DocuLib.getDocu(nodeConfig, "excludeFileName")));
String timeoutMillis1 = String.valueOf(flowRunDataDto.getFieldValue(DocuLib.getDocu(nodeConfig, "timeoutMillis")));
if (StrUtil.isEmpty(timeoutMillis1) || timeoutMillis1.startsWith("Document{")) {
timeoutMillis1 = "1000";
}
Integer timeoutMillis = Integer.parseInt(timeoutMillis1);//ftp ip校验超时时间毫秒
String ftpType = DocuLib.getStr(nodeConfig, "ftpType.id");
Document result = DocuLib.newDoc();
if (FlowSharedDirectoryUtils.pingHost(ipRow, timeoutMillis)) {
if (StrUtil.equals(ftpType, "1")) {
Document dto = FlowSharedDirectoryUtils.downloadFileByName(ipRow, directoryPath, username, password, localFilePath, fileExtension, excludeFileName, fileName);
result.putAll(dto);
} else if (StrUtil.equals(ftpType, "2")) {
Document dto = FlowSharedDirectoryUtils.vagueDownloadFile(ipRow, directoryPath, username, password, localFilePath, fileExtension, excludeFileName, fileName);
result.putAll(dto);
} else if (StrUtil.equals(ftpType, "4")) {
Document dto = FlowSharedDirectoryUtils.listDownloadGetLatestFile(ipRow, directoryPath, username, password, localFilePath, fileExtension, excludeFileName);
result.putAll(dto);
} else if (StrUtil.equals(ftpType, "5")) {
Object downloadedListObj = flowRunDataDto.getFieldValue(DocuLib.getDocu(nodeConfig, "downloadedList"));
List<String> downloadedList = null;
if (downloadedListObj instanceof List) {
try {
downloadedList = ((List<?>) downloadedListObj).stream()
.map(Object::toString)
.toList();
} catch (Exception e) {
result.append("message", "过滤已下载的文件请传List<String>");
}
}
Document dto = FlowSharedDirectoryUtils.listDownloadGetAllLatestDateFiles(ipRow, directoryPath, username, password, localFilePath, fileExtension, excludeFileName,downloadedList);
result.putAll(dto);
} else if (StrUtil.equals(ftpType, "6")) {
Document dto = FlowSharedDirectoryUtils.downloadAllFile(ipRow, directoryPath, username, password, localFilePath, fileExtension, excludeFileName);
result.putAll(dto);
} else {
Document dto = FlowSharedDirectoryUtils.listDownloadGetFirstFile(ipRow, directoryPath, username, password, localFilePath, fileExtension, excludeFileName);
result.putAll(dto);
}
} else {
result.append("message", "请检查网络情况,IP未通!");
}
flowRunDataDto.nodeValueAppend(new Document().append("result", result));
FlowProtocolAccessLogUtil.addProtocolReadLog(nodeConfig, result);
}
@Override
public void unRegister() {
}
}
java
复制代码
package com.skybird.iot.addons.flow.flowCreator.backend.util;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.RuntimeUtil;
import com.skybird.commons.mongo.DocuLib;
import org.bson.Document;
import java.io.*;
import java.net.InetAddress;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
public class FlowSharedDirectoryUtils {
/**
* 判断IP地址是否可达
*
* @param ip
* @param timeoutMillis 超时时间毫秒
* @return
*/
public static boolean isHostReachable(String ip, int timeoutMillis) {
try {
return InetAddress.getByName(ip).isReachable(timeoutMillis);
} catch (Exception e) {
return false;
}
}
/**
* 判断IP地址是否可达 ping方式
*
* @param ipOrHost
* @param timeoutMs
* @return
*/
public static boolean pingHost(String ipOrHost, int timeoutMs) {
String command = System.getProperty("os.name").toLowerCase().contains("win")
? "ping -n 1 -w " + timeoutMs + " " + ipOrHost
: "ping -c 1 -W " + (timeoutMs / 1000) + " " + ipOrHost;
String result = RuntimeUtil.execForStr(command);
return result.contains("TTL=") || result.contains("ttl=");
}
/**
* 从共享目录下载最新修改的指定后缀文件
*
* @param ip 服务器IP地址
* @param sharePath 共享路径
* @param username 用户名
* @param password 密码
* @param localPath 本地保存路径
* @param fileExtension 文件后缀(如 ".log",null表示不限制后缀)
* @param excludeFileName 要排除的文件名(如 "Total.log",null表示不排除任何文件)
* @return 下载的文件路径
* @throws IOException IO异常
*/
public static Document listDownloadGetLatestFile(String ip, String sharePath, String username, String password,
String localPath, String fileExtension, String excludeFileName) {
try {
// 构造完整的共享路径,自动识别路径分隔符
String fullSharePath = String.format("\\\\%s\\%s", ip, sharePath);
Path networkPath = Paths.get(fullSharePath);
// 检查共享目录是否存在
if (!Files.exists(networkPath)) {
// 尝试映射网络驱动器(在Windows环境下)
Document document = mapNetworkDrive(fullSharePath, username, password);
// 返回错误信息
if (DocuLib.isNotEmpty(document)) {
return document;
}
// 重新检查
if (!Files.exists(networkPath)) {
return DocuLib.newDoc("message", "无法访问共享目录: " + fullSharePath);
}
}
Path latestFile = findLatestFile(networkPath, fileExtension, excludeFileName);
if (latestFile == null) {
if (fileExtension != null) {
return DocuLib.newDoc("message", "无法找到最新" + fileExtension + "文件");
} else {
return DocuLib.newDoc("message", "无法找到最新文件");
}
}
// 创建目标目录
createDirectory(localPath);
// 获取文件名并构造目标路径,添加UUID避免冲突
String fileName = latestFile.getFileName().toString();
String fileNameWithoutExtension = getFileBaseName(fileName);
String fileExtensionName = getFileExtension(fileName);
String uniqueFileName = fileNameWithoutExtension + "_" + generateHexId() + fileExtensionName;
Path targetPath = Paths.get(localPath, uniqueFileName);
// 复制文件到本地
Files.copy(latestFile, targetPath, StandardCopyOption.REPLACE_EXISTING);
// 将修改时间转换为字符串格式
LocalDateTime modifiedTime = LocalDateTime.ofInstant(
Files.getLastModifiedTime(latestFile).toInstant(),
ZoneId.systemDefault()
);
String formattedTime = modifiedTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
Document dto = DocuLib.newDoc("filePath", targetPath.toString());
dto.put("updateTime", formattedTime);
return dto;
} catch (IOException e) {
e.printStackTrace();
return DocuLib.newDoc("message", "下载文件时发生错误: " + e.getMessage());
}
}
/**
* 从共享目录获取最新日期的所有文件下载到本地
*
* @param ip 服务器IP地址
* @param sharePath 共享路径
* @param username 用户名
* @param password 密码
* @param localPath 本地保存路径
* @param fileExtension 文件后缀(如 ".log",null表示不限制后缀)
* @param excludeFileName 要排除的文件名(如 "Total.log",null表示不排除任何文件)
* @param downloadedList 已经下载过的文件名列表
* @return 下载结果,包含所有下载文件的路径列表
* @throws IOException IO异常
*/
public static Document listDownloadGetAllLatestDateFiles(String ip, String sharePath, String username, String password,
String localPath, String fileExtension, String excludeFileName, List<String> downloadedList) {
try {
// 构造完整的共享路径,自动识别路径分隔符
String fullSharePath = String.format("\\\\%s\\%s", ip, sharePath);
Path networkPath = Paths.get(fullSharePath);
// 检查共享目录是否存在
if (!Files.exists(networkPath)) {
// 尝试映射网络驱动器(在Windows环境下)
Document document = mapNetworkDrive(fullSharePath, username, password);
// 返回错误信息
if (DocuLib.isNotEmpty(document)) {
return document;
}
// 重新检查
if (!Files.exists(networkPath)) {
return DocuLib.newDoc("message", "无法访问共享目录: " + fullSharePath);
}
}
// 查找最新日期的所有文件
List<Path> latestDateFiles = findAllLatestDateFiles(networkPath, fileExtension, excludeFileName);
// 过滤掉已下载的文件
if (CollUtil.isNotEmpty(downloadedList)) {
latestDateFiles.removeIf(file -> downloadedList.contains(file.getFileName().toString()));
}
if (latestDateFiles.isEmpty()) {
if (fileExtension != null) {
return DocuLib.newDoc("message", "无法找到最新日期的" + fileExtension + "文件");
} else {
return DocuLib.newDoc("message", "无法找到最新日期的文件");
}
}
// 创建目标目录
createDirectory(localPath);
List<String> downloadedFiles = new ArrayList<>();
String latestDate = "";
// 下载所有最新日期的文件
for (Path file : latestDateFiles) {
try {
// 获取文件名并构造目标路径,添加UUID避免冲突
String fileName = file.getFileName().toString();
String fileNameWithoutExtension = getFileBaseName(fileName);
String fileExtensionName = getFileExtension(fileName);
String uniqueFileName = fileNameWithoutExtension + "_" + generateHexId() + fileExtensionName;
Path targetPath = Paths.get(localPath, uniqueFileName);
// 复制文件到本地
Files.copy(file, targetPath, StandardCopyOption.REPLACE_EXISTING);
downloadedFiles.add(targetPath.toString());
// 获取最新日期信息(所有文件日期相同,只需获取一次)
if (latestDate.isEmpty()) {
LocalDateTime modifiedTime = LocalDateTime.ofInstant(
Files.getLastModifiedTime(file).toInstant(),
ZoneId.systemDefault()
);
latestDate = modifiedTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
}
} catch (IOException e) {
return DocuLib.newDoc("message", "下载文件时出错:" + file.getFileName() + " " + e.getMessage());
}
}
Document result = DocuLib.newDoc("filePath", downloadedFiles);
result.put("updateTime", latestDate);
return result;
} catch (Exception e) {
return DocuLib.newDoc("message", "下载文件时出错: " + e.getMessage());
}
}
/**
* 查找最新日期的所有文件(内存优化版本,适用于超大量文件场景)
*
* @param directory 目录路径
* @param fileExtension 文件后缀(如 ".log",null表示不限制后缀)
* @param excludeFileName 要排除的文件名(如 "Total.log",null表示不排除任何文件)
* @return 最新日期的所有文件路径列表
* @throws IOException IO异常
*/
private static List<Path> findAllLatestDateFiles(Path directory, String fileExtension, String excludeFileName) throws IOException {
// 使用单次遍历找出最新时间并收集文件
LocalDateTime[] latestDateTime = {null};
List<Path> latestDateFiles = new ArrayList<>();
Files.walkFileTree(directory, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
if (attrs.isRegularFile()) {
String fileName = file.getFileName().toString();
// 检查是否需要排除该文件
if (excludeFileName != null && fileName.equalsIgnoreCase(excludeFileName)) {
return FileVisitResult.CONTINUE; // 跳过该文件
}
// 检查文件后缀是否匹配
if (fileExtension == null || fileName.toLowerCase().endsWith(fileExtension.toLowerCase())) {
try {
LocalDateTime fileDateTime = LocalDateTime.ofInstant(
attrs.lastModifiedTime().toInstant(),
ZoneId.systemDefault()
).withNano(0); // 截断到秒级精度
// 如果发现新的最新时间,清空之前的列表并更新最新时间
if (latestDateTime[0] == null || fileDateTime.isAfter(latestDateTime[0])) {
latestDateTime[0] = fileDateTime;
latestDateFiles.clear(); // 清空之前的文件列表
latestDateFiles.add(file); // 添加当前文件
}
// 如果是相同时间的文件,添加到列表中
else if (fileDateTime.isEqual(latestDateTime[0])) {
latestDateFiles.add(file);
}
} catch (Exception e) {
// 忽略无法获取修改时间的文件
}
}
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
// 忽略无法访问的文件,继续处理其他文件
return FileVisitResult.CONTINUE;
}
});
return latestDateFiles;
}
/**
* 从共享目录根据文件名获取相同文件名的文件下载到本地
*
* @param ip 服务器IP地址
* @param sharePath 共享路径
* @param username 用户名
* @param password 密码
* @param localPath 本地保存路径
* @param fileExtension 文件后缀(如 ".log",null表示不限制后缀)
* @param excludeFileName 要排除的文件名(如 "Total.log",null表示不排除任何文件)
* @param fileName 文件名
* @return 下载的文件路径
*/
public static Document downloadFileByName(String ip, String sharePath, String username, String password,
String localPath, String fileExtension, String excludeFileName, String fileName) {
try {
// 构造完整的共享路径
String fullSharePath = String.format("\\\\%s\\%s", ip, sharePath);
Path networkPath = Paths.get(fullSharePath);
// 检查共享目录是否存在
if (!Files.exists(networkPath)) {
// 尝试映射网络驱动器(在Windows环境下)
Document document = mapNetworkDrive(fullSharePath, username, password);
// 返回错误信息
if (DocuLib.isNotEmpty(document)) {
return document;
}
// 重新检查
if (!Files.exists(networkPath)) {
return DocuLib.newDoc("message", "无法访问共享目录: " + fullSharePath);
}
}
// 查找指定文件名的文件
Path targetFile = findFileByName(networkPath, fileName, fileExtension, excludeFileName);
if (targetFile == null) {
return DocuLib.newDoc("message", "无法找到文件: " + fileName);
}
// 创建目标目录
createDirectory(localPath);
// 获取文件名并构造目标路径,添加UUID避免冲突
String targetFileName = targetFile.getFileName().toString();
String fileNameWithoutExtension = getFileBaseName(targetFileName);
String fileExtensionName = getFileExtension(targetFileName);
String uniqueFileName = fileNameWithoutExtension + "_" + generateHexId() + fileExtensionName;
Path targetPath = Paths.get(localPath, uniqueFileName);
// 复制文件到本地
Files.copy(targetFile, targetPath, StandardCopyOption.REPLACE_EXISTING);
// 将修改时间转换为字符串格式
LocalDateTime modifiedTime = LocalDateTime.ofInstant(
Files.getLastModifiedTime(targetFile).toInstant(),
ZoneId.systemDefault()
);
String formattedTime = modifiedTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
Document dto = DocuLib.newDoc("filePath", targetPath.toString());
dto.put("updateTime", formattedTime);
dto.put("fileName", targetFileName);
return dto;
} catch (Exception e) {
return DocuLib.newDoc("message", "下载文件时出错: " + e.getMessage());
}
}
/**
* 从共享目录获取第一个包含文件名的文件下载到本地
*
* @param ip 服务器IP地址
* @param sharePath 共享路径
* @param username 用户名
* @param password 密码
* @param localPath 本地保存路径
* @param fileExtension 文件后缀(如 ".log",null表示不限制后缀)
* @param excludeFileName 要排除的文件名(如 "Total.log",null表示不排除任何文件)
* @param fileName 文件名
* @return 下载的文件路径
*/
public static Document vagueDownloadFile(String ip, String sharePath, String username, String password,
String localPath, String fileExtension, String excludeFileName, String fileName) {
try {
// 构造完整的共享路径
String fullSharePath = String.format("\\\\%s\\%s", ip, sharePath);
Path networkPath = Paths.get(fullSharePath);
// 检查共享目录是否存在
if (!Files.exists(networkPath)) {
// 尝试映射网络驱动器(在Windows环境下)
Document document = mapNetworkDrive(fullSharePath, username, password);
// 返回错误信息
if (DocuLib.isNotEmpty(document)) {
return document;
}
// 重新检查
if (!Files.exists(networkPath)) {
return DocuLib.newDoc("message", "无法访问共享目录: " + fullSharePath);
}
}
// 查找包含指定文件名的文件
Path targetFile = findFileByVagueName(networkPath, fileName, fileExtension, excludeFileName);
if (targetFile == null) {
return DocuLib.newDoc("message", "无法找到包含文件名 '" + fileName + "' 的文件");
}
// 创建目标目录
createDirectory(localPath);
// 获取文件名并构造目标路径,添加UUID避免冲突
String targetFileName = targetFile.getFileName().toString();
String fileNameWithoutExtension = getFileBaseName(targetFileName);
String fileExtensionName = getFileExtension(targetFileName);
String uniqueFileName = fileNameWithoutExtension + "_" + generateHexId() + fileExtensionName;
Path targetPath = Paths.get(localPath, uniqueFileName);
// 复制文件到本地
Files.copy(targetFile, targetPath, StandardCopyOption.REPLACE_EXISTING);
// 将修改时间转换为字符串格式
LocalDateTime modifiedTime = LocalDateTime.ofInstant(
Files.getLastModifiedTime(targetFile).toInstant(),
ZoneId.systemDefault()
);
String formattedTime = modifiedTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
Document dto = DocuLib.newDoc("filePath", targetPath.toString());
dto.put("updateTime", formattedTime);
dto.put("fileName", targetFileName);
return dto;
} catch (Exception e) {
return DocuLib.newDoc("message", "下载文件时出错: " + e.getMessage());
}
}
/**
* 从共享目录根据文件目录获取第一个文件下载到本地
*
* @param ip 服务器IP地址
* @param sharePath 共享路径
* @param username 用户名
* @param password 密码
* @param localPath 本地保存路径
* @param fileExtension 文件后缀(如 ".log",null表示不限制后缀)
* @param excludeFileName 要排除的文件名(如 "Total.log",null表示不排除任何文件)
* @return 下载的文件路径
*/
public static Document listDownloadGetFirstFile(String ip, String sharePath, String username, String password,
String localPath, String fileExtension, String excludeFileName) {
try {
// 构造完整的共享路径
String fullSharePath = String.format("\\\\%s\\%s", ip, sharePath);
Path networkPath = Paths.get(fullSharePath);
// 检查共享目录是否存在
if (!Files.exists(networkPath)) {
// 尝试映射网络驱动器(在Windows环境下)
Document document = mapNetworkDrive(fullSharePath, username, password);
// 返回错误信息
if (DocuLib.isNotEmpty(document)) {
return document;
}
// 重新检查
if (!Files.exists(networkPath)) {
return DocuLib.newDoc("message", "无法访问共享目录: " + fullSharePath);
}
}
// 查找第一个文件
Path firstFile = findFirstFile(networkPath, fileExtension, excludeFileName);
if (firstFile == null) {
if (fileExtension != null) {
return DocuLib.newDoc("message", "无法找到" + fileExtension + "文件");
} else {
return DocuLib.newDoc("message", "无法找到文件");
}
}
// 创建目标目录
createDirectory(localPath);
// 获取文件名并构造目标路径,添加UUID避免冲突
String fileName = firstFile.getFileName().toString();
String fileNameWithoutExtension = getFileBaseName(fileName);
String fileExtensionName = getFileExtension(fileName);
String uniqueFileName = fileNameWithoutExtension + "_" + generateHexId() + fileExtensionName;
Path targetPath = Paths.get(localPath, uniqueFileName);
// 复制文件到本地
Files.copy(firstFile, targetPath, StandardCopyOption.REPLACE_EXISTING);
// 将修改时间转换为字符串格式
LocalDateTime modifiedTime = LocalDateTime.ofInstant(
Files.getLastModifiedTime(firstFile).toInstant(),
ZoneId.systemDefault()
);
String formattedTime = modifiedTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
Document dto = DocuLib.newDoc("filePath", targetPath.toString());
dto.put("updateTime", formattedTime);
dto.put("fileName", fileName);
return dto;
} catch (Exception e) {
return DocuLib.newDoc("message", "下载文件时出错: " + e.getMessage());
}
}
/**
* 从共享目录根据文件目录获取全部文件下载到本地
*
* @param ip 服务器IP地址
* @param sharePath 共享路径
* @param username 用户名
* @param password 密码
* @param localPath 本地保存路径
* @param fileExtension 文件后缀(如 ".log",null表示不限制后缀)
* @param excludeFileName 要排除的文件名(如 "Total.log",null表示不排除任何文件)
* @return 下载的文件路径
*/
public static Document downloadAllFile(String ip, String sharePath, String username, String password,
String localPath, String fileExtension, String excludeFileName) {
try {
// 构造完整的共享路径
String fullSharePath = String.format("\\\\%s\\%s", ip, sharePath);
Path networkPath = Paths.get(fullSharePath);
// 检查共享目录是否存在
if (!Files.exists(networkPath)) {
// 尝试映射网络驱动器(在Windows环境下)
Document document = mapNetworkDrive(fullSharePath, username, password);
// 返回错误信息
if (DocuLib.isNotEmpty(document)) {
return document;
}
// 重新检查
if (!Files.exists(networkPath)) {
return DocuLib.newDoc("message", "无法访问共享目录: " + fullSharePath);
}
}
// 查找所有符合条件的文件
List<Path> allFiles = findAllFiles(networkPath, fileExtension, excludeFileName);
if (allFiles.isEmpty()) {
if (fileExtension != null) {
return DocuLib.newDoc("message", "无法找到任何" + fileExtension + "文件");
} else {
return DocuLib.newDoc("message", "无法找到任何文件");
}
}
// 创建目标目录
createDirectory(localPath);
List<String> downloadedFiles = new ArrayList<>();
String latestDate = "";
// 下载所有文件
for (Path file : allFiles) {
try {
// 获取文件名并构造目标路径,添加UUID避免冲突
String fileName = file.getFileName().toString();
String fileNameWithoutExtension = getFileBaseName(fileName);
String fileExtensionName = getFileExtension(fileName);
String uniqueFileName = fileNameWithoutExtension + "_" + generateHexId() + fileExtensionName;
Path targetPath = Paths.get(localPath, uniqueFileName);
// 复制文件到本地
Files.copy(file, targetPath, StandardCopyOption.REPLACE_EXISTING);
downloadedFiles.add(targetPath.toString());
// 获取最新修改时间
LocalDateTime modifiedTime = LocalDateTime.ofInstant(
Files.getLastModifiedTime(file).toInstant(),
ZoneId.systemDefault()
);
String formattedTime = modifiedTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
// 更新最新的文件时间
if (latestDate.isEmpty() || formattedTime.compareTo(latestDate) > 0) {
latestDate = formattedTime;
}
} catch (IOException e) {
return DocuLib.newDoc("message", "下载文件时出错:" + file.getFileName() + " " + e.getMessage());
}
}
Document result = DocuLib.newDoc("filePath", downloadedFiles);
result.put("updateTime", latestDate);
return result;
} catch (Exception e) {
return DocuLib.newDoc("message", "下载文件时出错: " + e.getMessage());
}
}
/**
* 查找所有符合条件的文件
*
* @param directory 目录路径
* @param fileExtension 文件后缀(如 ".log",null表示不限制后缀)
* @param excludeFileName 要排除的文件名(如 "Total.log",null表示不排除任何文件)
* @return 符合条件的所有文件路径列表
* @throws IOException IO异常
*/
private static List<Path> findAllFiles(Path directory, String fileExtension, String excludeFileName) throws IOException {
List<Path> files = new ArrayList<>();
Files.walkFileTree(directory, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
if (attrs.isRegularFile()) {
String fileName = file.getFileName().toString();
// 检查是否需要排除该文件
if (excludeFileName != null && fileName.equalsIgnoreCase(excludeFileName)) {
return FileVisitResult.CONTINUE; // 跳过该文件
}
// 检查文件后缀是否匹配
if (fileExtension == null || fileName.toLowerCase().endsWith(fileExtension.toLowerCase())) {
files.add(file);
}
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
// 忽略无法访问的文件,继续处理其他文件
return FileVisitResult.CONTINUE;
}
});
return files;
}
/**
* 映射网络驱动器
*
* @param sharePath 共享路径(格式:\\\server\share)
* @param username 用户名
* @param password 密码
* @return 错误信息文档,如果成功则返回空文档
*/
private static Document mapNetworkDrive(String sharePath, String username, String password) {
Process process = null;
try {
// 1. 先尝试删除可能存在的旧连接(忽略错误)
try {
String deleteCommand = String.format("cmd.exe /c net use %s /delete /yes", sharePath);
Process deleteProcess = Runtime.getRuntime().exec(deleteCommand);
// 消耗掉所有输出,避免阻塞
consumeStream(deleteProcess.getInputStream());
consumeStream(deleteProcess.getErrorStream());
deleteProcess.waitFor();
} catch (Exception e) {
// 忽略删除连接的错误
}
// 2. 建立新的连接,使用 ProcessBuilder 避免命令注入和空格问题
// 使用数组形式传递参数,自动处理空格和特殊字符
ProcessBuilder processBuilder = new ProcessBuilder(
"cmd.exe",
"/c",
"net",
"use",
sharePath, // 路径本身(如 \\\\10.50.76.61\\My History)
password, // 密码
"/user:" + username, // 用户参数(如 /user:ftp)
"/persistent:no"
);
process = processBuilder.start();
// 异步读取输出流,避免阻塞
StreamGobbler errorGobbler = new StreamGobbler(process.getErrorStream(), "GBK");
StreamGobbler outputGobbler = new StreamGobbler(process.getInputStream(), "GBK");
errorGobbler.start();
outputGobbler.start();
int result = process.waitFor();
// 等待输出读取完成
errorGobbler.join(2000);
outputGobbler.join(2000);
if (result != 0) {
String errorMessage = errorGobbler.getOutput() + "\n" + outputGobbler.getOutput();
errorMessage = errorMessage.trim();
System.err.println("映射网络驱动器失败,退出码:" + result);
System.err.println("错误详情:" + errorMessage);
if (errorMessage.isEmpty()) {
errorMessage = "未知错误,请检查网络连接、共享服务器设置或凭据是否正确";
}
return DocuLib.newDoc("message", "映射网络驱动器失败:" + errorMessage);
} else {
System.out.println("成功映射网络驱动器:" + sharePath);
return DocuLib.newDoc();
}
} catch (Exception e) {
return DocuLib.newDoc("message", "映射网络驱动器时出错:" + e.getMessage());
}
}
/**
* 消耗流中的所有数据(用于避免进程阻塞)
*/
private static void consumeStream(InputStream inputStream) {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, "GBK"));
while (reader.read() != -1) {
// 只是消耗掉,不处理
}
reader.close();
} catch (Exception e) {
// 忽略异常
}
}
/**
* 用于异步读取进程输出的辅助类
*/
private static class StreamGobbler extends Thread {
private final InputStream inputStream;
private final String encoding;
private final StringBuilder output = new StringBuilder();
public StreamGobbler(InputStream inputStream, String encoding) {
this.inputStream = inputStream;
this.encoding = encoding;
}
@Override
public void run() {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, encoding));
String line;
while ((line = reader.readLine()) != null) {
output.append(line).append("\n");
}
reader.close();
} catch (Exception e) {
output.append("读取输出时出错:").append(e.getMessage());
}
}
public String getOutput() {
return output.toString().trim();
}
}
/**
* 获取文件名(不包含扩展名)
*
* @param fileName 完整文件名
* @return 文件名(不包含扩展名)
*/
private static String getFileBaseName(String fileName) {
int lastDotIndex = fileName.lastIndexOf('.');
if (lastDotIndex == -1) {
return fileName;
}
return fileName.substring(0, lastDotIndex);
}
/**
* 获取文件扩展名
*
* @param fileName 完整文件名
* @return 文件扩展名(包含点号)
*/
private static String getFileExtension(String fileName) {
int lastDotIndex = fileName.lastIndexOf('.');
if (lastDotIndex == -1) {
return "";
}
return fileName.substring(lastDotIndex);
}
/**
* 高效查找最新修改的文件
*
* @param directory 目录路径
* @param fileExtension 文件后缀(如 ".log",null表示不限制后缀)
* @param excludeFileName 要排除的文件名(如 "Total.log",null表示不排除任何文件)
* @return 最新修改的文件路径
* @throws IOException IO异常
*/
private static Path findLatestFile(Path directory, String fileExtension, String excludeFileName) throws IOException {
Path[] latest = {null};
long[] latestTime = {Long.MIN_VALUE};
Files.walkFileTree(directory, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
if (attrs.isRegularFile()) {
String fileName = file.getFileName().toString();
// 检查是否需要排除该文件
if (excludeFileName != null && fileName.equalsIgnoreCase(excludeFileName)) {
return FileVisitResult.CONTINUE; // 跳过该文件
}
// 检查文件后缀是否匹配
if (fileExtension == null || fileName.toLowerCase().endsWith(fileExtension.toLowerCase())) {
long modifiedTime = attrs.lastModifiedTime().toMillis();
if (modifiedTime > latestTime[0]) {
latestTime[0] = modifiedTime;
latest[0] = file;
}
}
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
// 忽略无法访问的文件,继续处理其他文件
return FileVisitResult.CONTINUE;
}
});
return latest[0];
}
/**
* 根据文件名查找文件
*
* @param directory 目录路径
* @param fileName 文件名
* @param fileExtension 文件后缀(如 ".log",null表示不限制后缀)
* @param excludeFileName 要排除的文件名(如 "Total.log",null表示不排除任何文件)
* @return 找到的文件路径
* @throws IOException IO异常
*/
private static Path findFileByName(Path directory, String fileName, String fileExtension, String excludeFileName) throws IOException {
Path[] targetFile = {null};
Files.walkFileTree(directory, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
if (attrs.isRegularFile()) {
String currentFileName = file.getFileName().toString();
// 检查是否需要排除该文件
if (excludeFileName != null && currentFileName.equalsIgnoreCase(excludeFileName)) {
return FileVisitResult.CONTINUE; // 跳过该文件
}
// 检查文件名是否匹配
if (currentFileName.equalsIgnoreCase(fileName)) {
// 检查文件后缀是否匹配
if (fileExtension == null || currentFileName.toLowerCase().endsWith(fileExtension.toLowerCase())) {
targetFile[0] = file;
return FileVisitResult.TERMINATE; // 找到文件后终止遍历
}
}
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
// 忽略无法访问的文件,继续处理其他文件
return FileVisitResult.CONTINUE;
}
});
return targetFile[0];
}
/**
* 根据模糊文件名查找文件
*
* @param directory 目录路径
* @param fileName 文件名(部分匹配)
* @param fileExtension 文件后缀(如 ".log",null表示不限制后缀)
* @param excludeFileName 要排除的文件名(如 "Total.log",null表示不排除任何文件)
* @return 找到的文件路径
* @throws IOException IO异常
*/
private static Path findFileByVagueName(Path directory, String fileName, String fileExtension, String excludeFileName) throws IOException {
Path[] targetFile = {null};
Files.walkFileTree(directory, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
if (attrs.isRegularFile()) {
String currentFileName = file.getFileName().toString();
// 检查是否需要排除该文件
if (excludeFileName != null && currentFileName.equalsIgnoreCase(excludeFileName)) {
return FileVisitResult.CONTINUE; // 跳过该文件
}
// 检查文件名是否包含指定字符串
if (currentFileName.toLowerCase().contains(fileName.toLowerCase())) {
// 检查文件后缀是否匹配
if (fileExtension == null || currentFileName.toLowerCase().endsWith(fileExtension.toLowerCase())) {
targetFile[0] = file;
return FileVisitResult.TERMINATE; // 找到文件后终止遍历
}
}
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
// 忽略无法访问的文件,继续处理其他文件
return FileVisitResult.CONTINUE;
}
});
return targetFile[0];
}
/**
* 查找第一个文件
*
* @param directory 目录路径
* @param fileExtension 文件后缀(如 ".log",null表示不限制后缀)
* @param excludeFileName 要排除的文件名(如 "Total.log",null表示不排除任何文件)
* @return 第一个文件路径
* @throws IOException IO异常
*/
private static Path findFirstFile(Path directory, String fileExtension, String excludeFileName) throws IOException {
Path[] firstFile = {null};
Files.walkFileTree(directory, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
if (attrs.isRegularFile()) {
String fileName = file.getFileName().toString();
// 检查是否需要排除该文件
if (excludeFileName != null && fileName.equalsIgnoreCase(excludeFileName)) {
return FileVisitResult.CONTINUE; // 跳过该文件
}
// 检查文件后缀是否匹配
if (fileExtension == null || fileName.toLowerCase().endsWith(fileExtension.toLowerCase())) {
firstFile[0] = file;
return FileVisitResult.TERMINATE; // 找到文件后终止遍历
}
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
// 忽略无法访问的文件,继续处理其他文件
return FileVisitResult.CONTINUE;
}
});
return firstFile[0];
}
/**
* 创建目录(如果不存在)
*
* @param directoryPath 目录路径
* @throws IOException IO异常
*/
public static void createDirectory(String directoryPath) throws IOException {
Path path = Paths.get(directoryPath);
if (!Files.exists(path)) {
Files.createDirectories(path);
}
}
/**
* 生成一个十六位数的ID
*
* @return 十六位数的ID
*/
private static String generateHexId() {
UUID uuid = UUID.randomUUID();
String uuidStr = uuid.toString().replace("-", ""); // 去掉UUID中的连字符
return uuidStr.substring(0, 16); // 截取前16位
}
}