更新版本:
1. 修复对话框图标显示 2. 文件名生成
This commit is contained in:
parent
0f54983b30
commit
98796bf123
|
|
@ -1,3 +1,3 @@
|
||||||
project_group =top.r3944realms.docchecktoolrefacored
|
project_group =top.r3944realms.docchecktoolrefacored
|
||||||
project_name = doc-check-tool
|
project_name = doc-check-tool
|
||||||
project_version = 1.0.0.3
|
project_version = 1.0.0.5
|
||||||
|
|
@ -12,7 +12,7 @@ public class JavaFxApplication extends Application {
|
||||||
@Override
|
@Override
|
||||||
public void init() throws Exception {
|
public void init() throws Exception {
|
||||||
super.init();
|
super.init();
|
||||||
System.setVersion("1.0.0.4");
|
System.setVersion("1.0.0.5");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,10 @@ package top.r3944realms.docchecktoolrefactored;
|
||||||
import javafx.stage.DirectoryChooser;
|
import javafx.stage.DirectoryChooser;
|
||||||
import javafx.stage.FileChooser;
|
import javafx.stage.FileChooser;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import top.r3944realms.docchecktoolrefactored.core.Setting;
|
import top.r3944realms.docchecktoolrefactored.core.Setting;
|
||||||
|
import top.r3944realms.docchecktoolrefactored.ui.module.ProjectInfoPaneController;
|
||||||
import top.r3944realms.docchecktoolrefactored.util.LoggerMarker;
|
import top.r3944realms.docchecktoolrefactored.util.LoggerMarker;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
|
@ -29,6 +31,8 @@ public enum System {
|
||||||
private static final long DEFAULT_SINGLE_TIMEOUT = 30;
|
private static final long DEFAULT_SINGLE_TIMEOUT = 30;
|
||||||
private static final long DEFAULT_TOTAL_TIMEOUT = 12600;
|
private static final long DEFAULT_TOTAL_TIMEOUT = 12600;
|
||||||
private static final boolean DEFAULT_ENABLE_STEP = false;
|
private static final boolean DEFAULT_ENABLE_STEP = false;
|
||||||
|
@Getter
|
||||||
|
private static final ProjectInfoPaneController.ProjectInfo projectInfo = new ProjectInfoPaneController.ProjectInfo();
|
||||||
|
|
||||||
public static void init() {
|
public static void init() {
|
||||||
loadSettings();
|
loadSettings();
|
||||||
|
|
|
||||||
|
|
@ -3,19 +3,29 @@ package top.r3944realms.docchecktoolrefactored.ui;
|
||||||
import javafx.event.ActionEvent;
|
import javafx.event.ActionEvent;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.scene.control.*;
|
import javafx.scene.control.*;
|
||||||
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.control.Menu;
|
||||||
|
import javafx.scene.control.MenuItem;
|
||||||
import javafx.scene.input.KeyCode;
|
import javafx.scene.input.KeyCode;
|
||||||
import javafx.scene.input.KeyCodeCombination;
|
import javafx.scene.input.KeyCodeCombination;
|
||||||
import javafx.scene.input.KeyCombination;
|
import javafx.scene.input.KeyCombination;
|
||||||
import javafx.scene.input.KeyEvent;
|
import javafx.scene.input.KeyEvent;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
import top.r3944realms.docchecktoolrefactored.System;
|
import top.r3944realms.docchecktoolrefactored.System;
|
||||||
import top.r3944realms.docchecktoolrefactored.core.Setting;
|
import top.r3944realms.docchecktoolrefactored.core.Setting;
|
||||||
import top.r3944realms.docchecktoolrefactored.ui.utils.DialogUtil;
|
import top.r3944realms.docchecktoolrefactored.ui.utils.DialogUtil;
|
||||||
import top.r3944realms.docchecktoolrefactored.util.LoggerMarker;
|
import top.r3944realms.docchecktoolrefactored.util.LoggerMarker;
|
||||||
|
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.StandardCopyOption;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
|
@ -161,14 +171,89 @@ public class MainStageController {
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
void onOpenHelpDoc(ActionEvent actionEvent) {
|
void onOpenHelpDoc(ActionEvent actionEvent) {
|
||||||
DialogUtil.showDetailedInformationDialog("帮助文档", "操作手册","见.exe程序同文件夹下的“操作手册。docx”文件。");
|
try {
|
||||||
|
// 获取帮助文档文件路径(如果不存在则从资源中释放)
|
||||||
|
File helpFile = getOrExtractHelpDocument();
|
||||||
|
|
||||||
|
if (helpFile.exists()) {
|
||||||
|
// 打开文档
|
||||||
|
openDocument(helpFile);
|
||||||
|
} else {
|
||||||
|
DialogUtil.showErrorDialog("打开失败", "帮助文档不存在",
|
||||||
|
"无法找到帮助文档,请联系技术支持。");
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
DialogUtil.showDetailedErrorDialog("打开失败", "无法打开帮助文档",
|
||||||
|
"错误信息:" + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取或从资源中提取帮助文档
|
||||||
|
*/
|
||||||
|
private File getOrExtractHelpDocument() throws IOException {
|
||||||
|
// 获取程序运行目录
|
||||||
|
String appDir = java.lang.System.getProperty("user.dir");
|
||||||
|
File helpFile = new File(appDir, HELP_DOC_FILE_NAME);
|
||||||
|
|
||||||
|
// 如果文件不存在,则从资源中复制出来
|
||||||
|
if (!helpFile.exists()) {
|
||||||
|
try (InputStream resourceStream = getClass().getResourceAsStream(HELP_DOC_RESOURCE_PATH)) {
|
||||||
|
if (resourceStream == null) {
|
||||||
|
throw new FileNotFoundException("资源中未找到帮助文档: " + HELP_DOC_RESOURCE_PATH);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 复制资源到程序目录
|
||||||
|
Files.copy(resourceStream, helpFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return helpFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 帮助文档在资源中的路径
|
||||||
|
private static final String HELP_DOC_RESOURCE_PATH = "/docs/UserHelpDocument.docx";
|
||||||
|
// 释放到外部的文件名
|
||||||
|
private static final String HELP_DOC_FILE_NAME = "操作手册.docx";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用系统默认程序打开文档
|
||||||
|
*/
|
||||||
|
private void openDocument(File file) throws IOException {
|
||||||
|
if (Desktop.isDesktopSupported()) {
|
||||||
|
Desktop desktop = Desktop.getDesktop();
|
||||||
|
if (desktop.isSupported(Desktop.Action.OPEN)) {
|
||||||
|
desktop.open(file);
|
||||||
|
} else {
|
||||||
|
throw new UnsupportedOperationException("系统不支持打开文件操作");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 对于不支持Desktop的环境(如某些Linux系统)
|
||||||
|
String os = java.lang.System.getProperty("os.name").toLowerCase();
|
||||||
|
ProcessBuilder pb = getProcessBuilder(file, os);
|
||||||
|
|
||||||
|
pb.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static @NotNull ProcessBuilder getProcessBuilder(File file, String os) {
|
||||||
|
ProcessBuilder pb;
|
||||||
|
|
||||||
|
if (os.contains("win")) {
|
||||||
|
pb = new ProcessBuilder("cmd.exe", "/c", file.getAbsolutePath());
|
||||||
|
} else if (os.contains("mac")) {
|
||||||
|
pb = new ProcessBuilder("open", file.getAbsolutePath());
|
||||||
|
} else if (os.contains("nix") || os.contains("nux")) {
|
||||||
|
pb = new ProcessBuilder("xdg-open", file.getAbsolutePath());
|
||||||
|
} else {
|
||||||
|
throw new UnsupportedOperationException("不支持的操作系统");
|
||||||
|
}
|
||||||
|
return pb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@FXML void onAbout(ActionEvent actionEvent) {
|
@FXML void onAbout(ActionEvent actionEvent) {
|
||||||
DialogUtil.showDetailedInformationDialog("版本", "版本信息","1.0.0-beta");
|
DialogUtil.showDetailedInformationDialog("关于软件", "数字化验收工具","软件版本:" + System.version() + "\n");
|
||||||
}
|
}
|
||||||
public void updateStepButtonsVisibility() {
|
public void updateStepButtonsVisibility() {
|
||||||
Setting setting = System.getSetting();
|
Setting setting = System.getSetting();
|
||||||
|
|
|
||||||
|
|
@ -135,7 +135,7 @@ public class PathCheckPaneController implements Initializable {
|
||||||
FileChooser fileChooser = System.getFileChooser();
|
FileChooser fileChooser = System.getFileChooser();
|
||||||
fileChooser.setTitle("选择保存逻辑地址文件的位置");
|
fileChooser.setTitle("选择保存逻辑地址文件的位置");
|
||||||
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("CSV Files", "*.csv"));
|
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("CSV Files", "*.csv"));
|
||||||
fileChooser.setInitialFileName(selectedMode.toString() + "逻辑地址文件.csv");
|
fileChooser.setInitialFileName(System.getProjectInfo().makeProjectInfoIntoFileNamePrefix() + "-" + selectedMode.toString() + "-逻辑地址文件 " + ".csv");
|
||||||
|
|
||||||
File outputFile = fileChooser.showSaveDialog(generateLogicalAddress2B.getScene().getWindow());
|
File outputFile = fileChooser.showSaveDialog(generateLogicalAddress2B.getScene().getWindow());
|
||||||
if (outputFile == null) {
|
if (outputFile == null) {
|
||||||
|
|
@ -241,7 +241,7 @@ public class PathCheckPaneController implements Initializable {
|
||||||
FileChooser fileChooser = System.getFileChooser();
|
FileChooser fileChooser = System.getFileChooser();
|
||||||
fileChooser.setTitle("选择保存物理地址文件的位置");
|
fileChooser.setTitle("选择保存物理地址文件的位置");
|
||||||
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("CSV Files", "*.csv"));
|
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("CSV Files", "*.csv"));
|
||||||
fileChooser.setInitialFileName(selectedMode.toString() + "物理地址文件.csv");
|
fileChooser.setInitialFileName(System.getProjectInfo().makeProjectInfoIntoFileNamePrefix() + "-" + selectedMode.toString() + "-物理地址文件.csv");
|
||||||
// 使用当前窗口作为父窗口显示文件选择对话框
|
// 使用当前窗口作为父窗口显示文件选择对话框
|
||||||
File outputFile = fileChooser.showSaveDialog(selectJPGFolder2B.getScene().getWindow());
|
File outputFile = fileChooser.showSaveDialog(selectJPGFolder2B.getScene().getWindow());
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,21 @@ package top.r3944realms.docchecktoolrefactored.ui.module;
|
||||||
|
|
||||||
import javafx.event.ActionEvent;
|
import javafx.event.ActionEvent;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.fxml.Initializable;
|
||||||
import javafx.scene.control.TextField;
|
import javafx.scene.control.TextField;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import top.r3944realms.docchecktoolrefactored.System;
|
||||||
import top.r3944realms.docchecktoolrefactored.ui.utils.DialogUtil;
|
import top.r3944realms.docchecktoolrefactored.ui.utils.DialogUtil;
|
||||||
|
import top.r3944realms.docchecktoolrefactored.util.FileUtil;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The type Project info pane controller.
|
* The type Project info pane controller.
|
||||||
*/
|
*/
|
||||||
public class ProjectInfoPaneController {
|
public class ProjectInfoPaneController implements Initializable {
|
||||||
@FXML private TextField projectNameTF;
|
@FXML private TextField projectNameTF;
|
||||||
@FXML private TextField AcceptanceTimeTF;
|
@FXML private TextField AcceptanceTimeTF;
|
||||||
@FXML private TextField totalCatalogNumberTF;
|
@FXML private TextField totalCatalogNumberTF;
|
||||||
|
|
@ -23,6 +31,46 @@ public class ProjectInfoPaneController {
|
||||||
totalCatalogNumberTF.clear();
|
totalCatalogNumberTF.clear();
|
||||||
AcceptanceTimeTF.clear();
|
AcceptanceTimeTF.clear();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void initialize(URL url, ResourceBundle resourceBundle) {
|
||||||
|
addAutoSaveListener(projectNameTF, "projectName");
|
||||||
|
addAutoSaveListener(AcceptanceTimeTF, "acceptanceTime");
|
||||||
|
addAutoSaveListener(totalCatalogNumberTF, "totalCatalogNumber");
|
||||||
|
addAutoSaveListener(fileCategoriesTF, "fileCategory");
|
||||||
|
addAutoSaveListener(fileYearTF, "fileYear");
|
||||||
|
}
|
||||||
|
private void addAutoSaveListener(TextField field, String fieldName) {
|
||||||
|
field.textProperty().addListener((observable, oldValue, newValue) -> {
|
||||||
|
switch (fieldName) {
|
||||||
|
case "projectName" -> System.getProjectInfo().setProjectName(newValue);
|
||||||
|
case "acceptanceTime" -> System.getProjectInfo().setAcceptanceTime(newValue);
|
||||||
|
case "totalCatalogNumber" -> System.getProjectInfo().setTotalCatalogNumber(newValue);
|
||||||
|
case "fileCategory" -> System.getProjectInfo().setFileCategory(newValue);
|
||||||
|
case "fileYear" -> System.getProjectInfo().setFileYear(newValue);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
@Setter @Getter
|
||||||
|
public static class ProjectInfo {
|
||||||
|
private String projectName;
|
||||||
|
private String acceptanceTime;
|
||||||
|
private String totalCatalogNumber;
|
||||||
|
private String fileCategory;
|
||||||
|
private String fileYear;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "ProjectInfo{" +
|
||||||
|
"projectName='" + projectName + '\'' +
|
||||||
|
", acceptanceTime='" + acceptanceTime + '\'' +
|
||||||
|
", totalCatalogNumber='" + totalCatalogNumber + '\'' +
|
||||||
|
", fileCategory='" + fileCategory + '\'' +
|
||||||
|
", fileYear='" + fileYear + '\'' +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
public String makeProjectInfoIntoFileNamePrefix() {
|
||||||
|
return FileUtil.ensureValidFileName(projectName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -207,7 +207,7 @@ public class StorageCarrierPaneController {
|
||||||
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("CSV Files", "*.csv"));
|
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("CSV Files", "*.csv"));
|
||||||
|
|
||||||
// 设置默认文件名
|
// 设置默认文件名
|
||||||
fileChooser.setInitialFileName("哈希值列表文件.csv");
|
fileChooser.setInitialFileName(System.getProjectInfo().makeProjectInfoIntoFileNamePrefix() + "-哈希值列表文件.csv");
|
||||||
|
|
||||||
// 使用当前窗口作为父窗口显示文件选择对话框
|
// 使用当前窗口作为父窗口显示文件选择对话框
|
||||||
File outputFile = fileChooser.showSaveDialog(selectLoadDigitalOutcomes7B.getScene().getWindow());
|
File outputFile = fileChooser.showSaveDialog(selectLoadDigitalOutcomes7B.getScene().getWindow());
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,10 @@ public class DuplicateDocumentDetectionTask extends Task<String>{
|
||||||
if (total > 0) {
|
if (total > 0) {
|
||||||
// 控制更新频率
|
// 控制更新频率
|
||||||
String msg = switch (phase) {
|
String msg = switch (phase) {
|
||||||
case GROUP_BY_SIZE -> String.format("正在按文件大小分组: %d/%d", current, total);
|
case GROUP_BY_SIZE -> {
|
||||||
|
totalFiles.getAndIncrement();
|
||||||
|
yield String.format("正在按文件大小分组: %d/%d", current, total);
|
||||||
|
}
|
||||||
case CALCULATE_HASH -> String.format("正在计算哈希值: %d/%d", current, total);
|
case CALCULATE_HASH -> String.format("正在计算哈希值: %d/%d", current, total);
|
||||||
};
|
};
|
||||||
updateMessage(msg);
|
updateMessage(msg);
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import javafx.scene.layout.GridPane;
|
||||||
import javafx.scene.layout.Priority;
|
import javafx.scene.layout.Priority;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
import javafx.stage.Window;
|
import javafx.stage.Window;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
import top.r3944realms.docchecktoolrefactored.Main;
|
import top.r3944realms.docchecktoolrefactored.Main;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
@ -23,7 +24,7 @@ public class DialogUtil {
|
||||||
).toExternalForm();
|
).toExternalForm();
|
||||||
|
|
||||||
private static final Image DIALOG_ICON = new Image(
|
private static final Image DIALOG_ICON = new Image(
|
||||||
Objects.requireNonNull(Main.class.getResourceAsStream("/img/logo256x.ico"))
|
Objects.requireNonNull(Main.class.getResourceAsStream("/img/icon.jpg"))
|
||||||
);
|
);
|
||||||
/**
|
/**
|
||||||
* Show exit confirmation boolean.
|
* Show exit confirmation boolean.
|
||||||
|
|
@ -321,10 +322,12 @@ public class DialogUtil {
|
||||||
dialogPane.getStyleClass().add("dialog-pane");
|
dialogPane.getStyleClass().add("dialog-pane");
|
||||||
}
|
}
|
||||||
// 封装设置Alert图标方法
|
// 封装设置Alert图标方法
|
||||||
private static void setAlertIcon(Alert alert) {
|
private static void setAlertIcon(@NotNull Alert alert) {
|
||||||
// 间接获取Alert对应的Stage
|
Platform.runLater(() -> {
|
||||||
Stage alertStage = (Stage) alert.getDialogPane().getScene().getWindow();
|
Stage alertStage = (Stage) alert.getDialogPane().getScene().getWindow();
|
||||||
// 添加图标(可添加多个尺寸,系统自动适配)
|
if (alertStage != null) {
|
||||||
alertStage.getIcons().add(DIALOG_ICON);
|
alertStage.getIcons().add(DIALOG_ICON);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -95,4 +95,84 @@ public class FileUtil {
|
||||||
|
|
||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* 检查文件名是否合法(跨平台)
|
||||||
|
* @param fileName 文件名(不含路径)
|
||||||
|
* @return true 合法;false 非法
|
||||||
|
*/
|
||||||
|
public static boolean isValidFileName(String fileName) {
|
||||||
|
if (fileName == null || fileName.isBlank()) return false;
|
||||||
|
|
||||||
|
// Windows 非法字符
|
||||||
|
String invalidChars = "<>:\"/\\\\|?*";
|
||||||
|
for (char c : invalidChars.toCharArray()) {
|
||||||
|
if (fileName.indexOf(c) >= 0) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Windows 保留名称
|
||||||
|
List<String> reservedNames = Arrays.asList(
|
||||||
|
"CON", "PRN", "AUX", "NUL",
|
||||||
|
"COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
|
||||||
|
"LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9"
|
||||||
|
);
|
||||||
|
String upperName = fileName.toUpperCase(Locale.ROOT);
|
||||||
|
String nameWithoutExt = upperName.contains(".") ? upperName.substring(0, upperName.indexOf('.')) : upperName;
|
||||||
|
if (reservedNames.contains(nameWithoutExt)) return false;
|
||||||
|
|
||||||
|
// 不能以空格或句点结尾
|
||||||
|
if (fileName.endsWith(" ") || fileName.endsWith(".")) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自动修复非法文件名:
|
||||||
|
* - 替换非法字符为下划线
|
||||||
|
* - 去除末尾空格和句点
|
||||||
|
* - 避免使用系统保留名
|
||||||
|
* @param fileName 原始文件名
|
||||||
|
* @return 修复后的安全文件名
|
||||||
|
*/
|
||||||
|
public static String sanitizeFileName(String fileName) {
|
||||||
|
if (fileName == null || fileName.isBlank()) {
|
||||||
|
return "untitled";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 替换非法字符为下划线
|
||||||
|
String sanitized = fileName.replaceAll("[<>:\"/\\\\|?*]", "_");
|
||||||
|
|
||||||
|
// 去掉结尾空格和句点
|
||||||
|
sanitized = sanitized.replaceAll("[ \\.]+$", "");
|
||||||
|
|
||||||
|
// Windows 保留名修复
|
||||||
|
List<String> reservedNames = Arrays.asList(
|
||||||
|
"CON", "PRN", "AUX", "NUL",
|
||||||
|
"COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
|
||||||
|
"LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9"
|
||||||
|
);
|
||||||
|
String upperName = sanitized.toUpperCase(Locale.ROOT);
|
||||||
|
String nameWithoutExt = upperName.contains(".") ? upperName.substring(0, upperName.indexOf('.')) : upperName;
|
||||||
|
if (reservedNames.contains(nameWithoutExt)) {
|
||||||
|
sanitized = "_" + sanitized; // 添加前缀防止冲突
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果修复后为空,则命名为 untitled
|
||||||
|
if (sanitized.isBlank()) sanitized = "untitled";
|
||||||
|
|
||||||
|
return sanitized;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验或修复文件名:
|
||||||
|
* 若合法则直接返回;
|
||||||
|
* 若非法则自动修复并打印日志
|
||||||
|
*/
|
||||||
|
public static String ensureValidFileName(String fileName) {
|
||||||
|
if (isValidFileName(fileName)) {
|
||||||
|
return fileName;
|
||||||
|
}
|
||||||
|
String sanitized = sanitizeFileName(fileName);
|
||||||
|
log.warn(LoggerMarker.TRACE_MARKER, "非法文件名已修复: {} → {}", fileName, sanitized);
|
||||||
|
return sanitized;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
BIN
src/main/resources/docs/UserHelpDocument.docx
Normal file
BIN
src/main/resources/docs/UserHelpDocument.docx
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user