提交代码

This commit is contained in:
lilin 2025-03-31 09:13:41 +08:00
parent bdac9db39a
commit 2520417014

View File

@ -21,6 +21,7 @@ import java.util.stream.Collectors;
import cn.hutool.json.JSONUtil; import cn.hutool.json.JSONUtil;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.services.s3.model.S3Object; import com.amazonaws.services.s3.model.S3Object;
import com.amazonaws.services.s3.model.S3ObjectInputStream; import com.amazonaws.services.s3.model.S3ObjectInputStream;
import com.amazonaws.util.IOUtils; import com.amazonaws.util.IOUtils;
@ -412,7 +413,13 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
files1.setUploadTime(tsFiles.getUploadTime()); files1.setUploadTime(tsFiles.getUploadTime());
files1.setUploader(loginuser.getUsername()); files1.setUploader(loginuser.getUsername());
files1.setFileName(name); files1.setFileName(name);
files1.setFileSize(sizeStr); if ("0.000".equals(sizeStr)) {
files1.setFileSize("0.001");
} else {
files1.setFileSize(sizeStr);
}
filesToSave.add(files1); filesToSave.add(files1);
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
LOGGER.error("文件大小必须是有效的数字"); LOGGER.error("文件大小必须是有效的数字");
@ -2235,6 +2242,7 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
} }
return destRoot.toFile(); return destRoot.toFile();
} }
// 转换Unix权限位 // 转换Unix权限位
private String getPosixMode(int mode) { private String getPosixMode(int mode) {
return String.format("%s%s%s %s%s%s %s%s%s", return String.format("%s%s%s %s%s%s %s%s%s",
@ -2242,6 +2250,7 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
(mode & 0040) != 0 ? "r" : "-", (mode & 0020) != 0 ? "w" : "-", (mode & 0010) != 0 ? "x" : "-", (mode & 0040) != 0 ? "r" : "-", (mode & 0020) != 0 ? "w" : "-", (mode & 0010) != 0 ? "x" : "-",
(mode & 0004) != 0 ? "r" : "-", (mode & 0002) != 0 ? "w" : "-", (mode & 0001) != 0 ? "x" : "-"); (mode & 0004) != 0 ? "r" : "-", (mode & 0002) != 0 ? "w" : "-", (mode & 0001) != 0 ? "x" : "-");
} }
/** /**
* 验证目标路径安全性防止路径穿越攻击 * 验证目标路径安全性防止路径穿越攻击
*/ */
@ -2286,7 +2295,8 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
/** /**
* 智能规范化压缩包内路径 * 智能规范化压缩包内路径
* @param entryPath 压缩包内原始路径 *
* @param entryPath 压缩包内原始路径
* @param archiveFilename 压缩包文件名"111.tar.gz" * @param archiveFilename 压缩包文件名"111.tar.gz"
* @return 处理后的路径 * @return 处理后的路径
*/ */
@ -2308,7 +2318,8 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
/** /**
* 安全规范化压缩包内路径 * 安全规范化压缩包内路径
* @param entryPath 原始路径 "111/222/3.txt" *
* @param entryPath 原始路径 "111/222/3.txt"
* @param archiveName 压缩包文件名 "111.7z" * @param archiveName 压缩包文件名 "111.7z"
* @return 规范化后的路径 "222/3.txt" * @return 规范化后的路径 "222/3.txt"
*/ */
@ -2382,7 +2393,8 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
/** /**
* 智能路径规范化修复7z/tar.gz多层级问题 * 智能路径规范化修复7z/tar.gz多层级问题
* @param entryPath 压缩包内原始路径 "111/222/3.txt" *
* @param entryPath 压缩包内原始路径 "111/222/3.txt"
* @param archiveName 压缩包文件名 "111.7z" * @param archiveName 压缩包文件名 "111.7z"
* @return 规范化后的路径 "222/3.txt" * @return 规范化后的路径 "222/3.txt"
*/ */
@ -2413,7 +2425,6 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
} }
/** /**
* 设置文件权限兼容Unix系统 * 设置文件权限兼容Unix系统
* *
@ -2561,7 +2572,41 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
List<FileItemResult> files = service.fileListData(path, fileName + "/"); List<FileItemResult> files = service.fileListData(path, fileName + "/");
if (files != null) { if (files != null) {
// 对每个文件的路径进行规范化 // 对每个文件的路径进行规范化
for (FileItemResult file : files) { // for (FileItemResult file : files) {
// String normalizedPath = ensurePathFormat(file.getPath());
// String ProcessingPath = processingPath(normalizedPath, nodeId);
// file.setPath(ProcessingPath);
// if (file.getSize() == null) {
// file.setSize((long) 0);
// } else {
// // 获取文件大小字节
// long fileSizeInBytes = file.getSize();
// // 转换为 MB 并保留两位小数
// double fileSizeInMB = fileSizeInBytes / (1024.0 * 1024.0);
// String fileSizeFormatted = String.format("%.2f", fileSizeInMB); // 保留两位小数
// // 判断是否为 "0.00"如果是则直接设置为 0
// if ("0.00".equals(fileSizeFormatted)) {
// file.setSize((long) 0); // 如果文件大小为 0.00直接设置为 0
// } else {
// // 否则将文件大小转换为 long去掉小数部分
// file.setSize((long) Double.parseDouble(fileSizeFormatted));
// }
// }
//
// QueryWrapper<TsFiles> queryWrapper = new QueryWrapper<>();
// queryWrapper.eq("node_id", nodeId);
// queryWrapper.eq("task_id", taskId);
// queryWrapper.eq("backup_path", normalizedPath);
// queryWrapper.eq("file_name", file.getName());
// TsFiles tsFiles1 = tsFilesMapper.selectOne(queryWrapper);
// if (tsFiles1 != null) {
// file.setParentId(tsFiles1.getParentId());
// file.setId(tsFiles1.getId());
// }
// }
files.forEach(file -> {
String normalizedPath = ensurePathFormat(file.getPath()); String normalizedPath = ensurePathFormat(file.getPath());
String ProcessingPath = processingPath(normalizedPath, nodeId); String ProcessingPath = processingPath(normalizedPath, nodeId);
file.setPath(ProcessingPath); file.setPath(ProcessingPath);
@ -2575,7 +2620,7 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
String fileSizeFormatted = String.format("%.2f", fileSizeInMB); // 保留两位小数 String fileSizeFormatted = String.format("%.2f", fileSizeInMB); // 保留两位小数
// 判断是否为 "0.00"如果是则直接设置为 0 // 判断是否为 "0.00"如果是则直接设置为 0
if ("0.00".equals(fileSizeFormatted)) { if ("0.00".equals(fileSizeFormatted)) {
file.setSize((long) 0); // 如果文件大小为 0.00直接设置为 0 file.setSize((long) 0.001); // 如果文件大小为 0.00直接设置为 0
} else { } else {
// 否则将文件大小转换为 long去掉小数部分 // 否则将文件大小转换为 long去掉小数部分
file.setSize((long) Double.parseDouble(fileSizeFormatted)); file.setSize((long) Double.parseDouble(fileSizeFormatted));
@ -2592,22 +2637,17 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
file.setParentId(tsFiles1.getParentId()); file.setParentId(tsFiles1.getParentId());
file.setId(tsFiles1.getId()); file.setId(tsFiles1.getId());
} }
} });
// files.forEach(file -> {
//
// });
// 同步添加线程安全 // 同步添加线程安全
synchronized (targetList) {
targetList.addAll(files); targetList.addAll(files);
}
} }
} else { } else {
List<FileItemResult> files = service.fileListData(path, fileName); List<FileItemResult> files = service.fileListData(path, fileName);
if (files != null) { if (files != null) {
// 对每个文件的路径进行规范化 // 对每个文件的路径进行规范化
for (FileItemResult file : files) { files.forEach(file -> {
String normalizedPath = ensurePathFormat(file.getPath()); String normalizedPath = ensurePathFormat(file.getPath());
String ProcessingPath = processingPath(normalizedPath, nodeId); String ProcessingPath = processingPath(normalizedPath, nodeId);
@ -2622,7 +2662,7 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
String fileSizeFormatted = String.format("%.2f", fileSizeInMB); // 保留两位小数 String fileSizeFormatted = String.format("%.2f", fileSizeInMB); // 保留两位小数
// 判断是否为 "0.00"如果是则直接设置为 0 // 判断是否为 "0.00"如果是则直接设置为 0
if ("0.00".equals(fileSizeFormatted)) { if ("0.00".equals(fileSizeFormatted)) {
file.setSize((long) 0); // 如果文件大小为 0.00直接设置为 0 file.setSize((long) 0.001); // 如果文件大小为 0.00直接设置为 0
} else { } else {
// 否则将文件大小转换为 long去掉小数部分 // 否则将文件大小转换为 long去掉小数部分
file.setSize((long) Double.parseDouble(fileSizeFormatted)); file.setSize((long) Double.parseDouble(fileSizeFormatted));
@ -2639,11 +2679,11 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
file.setParentId(tsFiles1.getParentId()); file.setParentId(tsFiles1.getParentId());
file.setId(tsFiles1.getId()); file.setId(tsFiles1.getId());
} }
} });
// 同步添加线程安全 // 同步添加线程安全
synchronized (targetList) {
targetList.addAll(files); targetList.addAll(files);
}
} }
} }
} }
@ -2673,11 +2713,19 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
// 辅助方法去重文件列表并行安全 // 辅助方法去重文件列表并行安全
private void deduplicateFileList(List<FileItemResult> fileList) { private void deduplicateFileList(List<FileItemResult> fileList) {
ConcurrentMap<String, Boolean> seenKeys = new ConcurrentHashMap<>(); // ConcurrentMap<String, Boolean> seenKeys = new ConcurrentHashMap<>();
fileList.removeIf(file -> { // fileList.removeIf(file -> {
String key = normalizePath(file.getPath()) + file.getName(); // String key = normalizePath(file.getPath()) + file.getName();
return seenKeys.putIfAbsent(key, Boolean.TRUE) != null; // return seenKeys.putIfAbsent(key, Boolean.TRUE) != null;
}); // });
ConcurrentHashMap<String, Boolean> seenKeys = new ConcurrentHashMap<>();
List<FileItemResult> uniqueFiles = fileList.parallelStream()
.filter(file -> seenKeys.putIfAbsent(generateMapKey(file), true) == null)
.collect(Collectors.toList());
fileList.clear();
fileList.addAll(uniqueFiles);
} }
public TsFiles compareFiles(List<FileItemResult> minioFiles, public TsFiles compareFiles(List<FileItemResult> minioFiles,
@ -2820,19 +2868,39 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
// 辅助方法获取 MinIO 文件 MD5 // 辅助方法获取 MinIO 文件 MD5
private String getMinioMD5(FileItemResult file, String bucketName) { private String getMinioMD5(FileItemResult file, String bucketName) {
// AbstractBaseFileService<?> minioService = storageSourceContext.getByStorageKey("minio");
// String key = StringUtils.concat(file.getPath(), file.getName());
//
// try {
// if (file.getSize() <= 5L * 1024 * 1024 * 1024) {
// return minioService.getObjectMetadata(bucketName, key).getETag().replace("\"", "");
// } else {
// try (S3Object s3Object = minioService.getObject(bucketName, key)) {
// return calculateMD5(s3Object.getObjectContent());
// }
// }
// } catch (Exception e) {
// LOGGER.error("MinIO MD5获取失败: {}", key, e);
// return null;
// }
AbstractBaseFileService<?> minioService = storageSourceContext.getByStorageKey("minio"); AbstractBaseFileService<?> minioService = storageSourceContext.getByStorageKey("minio");
String key = StringUtils.concat(file.getPath(), file.getName()); String key = normalizePath(file.getPath()) + file.getName();
try { try {
if (file.getSize() <= 5L * 1024 * 1024 * 1024) { ObjectMetadata metadata = minioService.getObjectMetadata(bucketName, key);
return minioService.getObjectMetadata(bucketName, key).getETag().replace("\"", ""); String eTag = metadata.getETag();
// 单部分上传的 ETag 是完整 MD5可直接使用
if (eTag != null && !eTag.contains("-")) {
return eTag.replace("\"", "");
} else { } else {
try (S3Object s3Object = minioService.getObject(bucketName, key)) { // 多部分上传需重新计算
return calculateMD5(s3Object.getObjectContent()); try (InputStream stream = minioService.getObject(bucketName, key).getObjectContent()) {
return calculateMD5(stream);
} }
} }
} catch (Exception e) { } catch (Exception e) {
LOGGER.error("MinIO MD5获取失败: {}", key, e); LOGGER.error("Failed to get MinIO MD5: {}", key, e);
return null; return null;
} }
} }
@ -4002,15 +4070,14 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
FileItemResult fileItemResult = fileService.getFileItem(path); FileItemResult fileItemResult = fileService.getFileItem(path);
if (fileItemResult != null || fileItemResult.getName() != null) { if (fileItemResult != null || fileItemResult.getName() != null) {
dto.setUrl(fileItemResult.getUrl()); dto.setUrl(fileItemResult.getUrl());
String ProcessingPath = processingPath(fileItemResult.getPath(), node.getNodeId()); dto.setType(fileItemResult.getType().getValue());
dto.setPath(ProcessingPath); // //如果是压缩文件 类型就给zip
//如果是压缩文件 类型就给zip // boolean isValid = hasValidExtension(fileItemResult.getName(), sysDictionaryItems);
boolean isValid = hasValidExtension(fileItemResult.getName(), sysDictionaryItems); // if (isValid) {
if (isValid) { // dto.setType("ZIP");
dto.setType("ZIP"); // } else {
} else { // dto.setType(fileItemResult.getType().getValue());
dto.setType(fileItemResult.getType().getValue()); // }
}
} else { } else {
dto.setUrl(null); dto.setUrl(null);
dto.setType(null); dto.setType(null);
@ -4037,13 +4104,14 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
} else { } else {
dto.setUrl(fileItemResult.getUrl()); dto.setUrl(fileItemResult.getUrl());
//如果是压缩文件 类型就给zip dto.setType(fileItemResult.getType().getValue());
boolean isValid = hasValidExtension(fileItemResult.getName(), sysDictionaryItems); // //如果是压缩文件 类型就给zip
if (isValid) { // boolean isValid = hasValidExtension(fileItemResult.getName(), sysDictionaryItems);
dto.setType("ZIP"); // if (isValid) {
} else { // dto.setType("ZIP");
dto.setType(fileItemResult.getType().getValue()); // } else {
} // dto.setType(fileItemResult.getType().getValue());
// }
} }
} }
} }
@ -4206,11 +4274,13 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
*/ */
@Override @Override
public String readFileContent(String id) throws IOException { public String readFileContent(String id) throws IOException {
StorageSourceConfig config = storageSourceConfigMapper.selectOne(new QueryWrapper<StorageSourceConfig>().eq("name", "filePath")); StorageSourceConfig config = storageSourceConfigMapper.selectOne(new QueryWrapper<StorageSourceConfig>().eq("name", "filePath"));
TsFiles tsFiles = tsFilesMapper.selectById(id); TsFiles tsFiles = tsFilesMapper.selectById(id);
// 1. 路径标准化与安全校验 // 1. 路径标准化与安全校验
Path targetPath = validateAndNormalizePath(config.getValue() + tsFiles.getWorkPath() + tsFiles.getFileName()); Path targetPath = validateAndNormalizePath(config.getValue() + tsFiles.getWorkPath() + tsFiles.getFileName());
StringBuilder content = new StringBuilder(); StringBuilder content = new StringBuilder();
// 使用缓冲流读取大文件减少内存占用 // 使用缓冲流读取大文件减少内存占用
@ -4276,7 +4346,7 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
* 返回值说明: com.yfd.platform.config.ResponseResult操作结果 * 返回值说明: com.yfd.platform.config.ResponseResult操作结果
***********************************/ ***********************************/
@Override @Override
public void batchUpdateFile(String id, List<ModifyCommand> modifications)throws IOException { public void batchUpdateFile(String id, List<ModifyCommand> modifications) throws IOException {
StorageSourceConfig config = storageSourceConfigMapper.selectOne(new QueryWrapper<StorageSourceConfig>().eq("name", "filePath")); StorageSourceConfig config = storageSourceConfigMapper.selectOne(new QueryWrapper<StorageSourceConfig>().eq("name", "filePath"));
TsFiles tsFile = tsFilesMapper.selectById(id); TsFiles tsFile = tsFilesMapper.selectById(id);
if (tsFile == null) { if (tsFile == null) {
@ -4334,7 +4404,8 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
/** /**
* 应用所有修改到文件内容 * 应用所有修改到文件内容
* @param lines 文件每一行的内容 *
* @param lines 文件每一行的内容
* @param modifications 修改指令集合 * @param modifications 修改指令集合
*/ */
private void applyModifications(List<String> lines, List<ModifyCommand> modifications) { private void applyModifications(List<String> lines, List<ModifyCommand> modifications) {