提交代码

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