diff --git a/src/main/java/com/example/demo/common/utils/FileUtil.java b/src/main/java/com/example/demo/common/utils/FileUtil.java new file mode 100644 index 0000000..49988f5 --- /dev/null +++ b/src/main/java/com/example/demo/common/utils/FileUtil.java @@ -0,0 +1,315 @@ +package com.example.demo.common.utils; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.nio.file.*; +import java.nio.file.attribute.BasicFileAttributes; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +/** + * 文件操作工具类 + * 提供文件读写、复制、删除等通用功能 + * + * @author 岳佳君 (2025年09月26日 13:48:12) + * @version 1.0.0 + */ +public class FileUtil { + + private static final Logger logger = LoggerFactory.getLogger(FileUtil.class); + + /** + * 读取文件内容 + * + * @param filePath 文件路径 + * @return 文件内容 + * @throws IOException IO异常 + */ + public static String readFileContent(String filePath) throws IOException { + return new String(Files.readAllBytes(Paths.get(filePath)), StandardCharsets.UTF_8); + } + + /** + * 写入内容到文件 + * + * @param filePath 文件路径 + * @param content 文件内容 + * @throws IOException IO异常 + */ + public static void writeFileContent(String filePath, String content) throws IOException { + // 确保父目录存在 + Path path = Paths.get(filePath); + Path parentDir = path.getParent(); + if (parentDir != null) { + Files.createDirectories(parentDir); + } + // 写入文件 + Files.write(path, content.getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); + } + + /** + * 追加内容到文件 + * + * @param filePath 文件路径 + * @param content 文件内容 + * @throws IOException IO异常 + */ + public static void appendFileContent(String filePath, String content) throws IOException { + // 确保父目录存在 + Path path = Paths.get(filePath); + Path parentDir = path.getParent(); + if (parentDir != null) { + Files.createDirectories(parentDir); + } + // 追加文件 + Files.write(path, content.getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE, StandardOpenOption.APPEND); + } + + /** + * 复制文件 + * + * @param sourceFilePath 源文件路径 + * @param targetFilePath 目标文件路径 + * @throws IOException IO异常 + */ + public static void copyFile(String sourceFilePath, String targetFilePath) throws IOException { + Path sourcePath = Paths.get(sourceFilePath); + Path targetPath = Paths.get(targetFilePath); + + // 确保目标目录存在 + Path targetDir = targetPath.getParent(); + if (targetDir != null) { + Files.createDirectories(targetDir); + } + + // 复制文件 + Files.copy(sourcePath, targetPath, StandardCopyOption.REPLACE_EXISTING); + } + + /** + * 移动文件 + * + * @param sourceFilePath 源文件路径 + * @param targetFilePath 目标文件路径 + * @throws IOException IO异常 + */ + public static void moveFile(String sourceFilePath, String targetFilePath) throws IOException { + Path sourcePath = Paths.get(sourceFilePath); + Path targetPath = Paths.get(targetFilePath); + + // 确保目标目录存在 + Path targetDir = targetPath.getParent(); + if (targetDir != null) { + Files.createDirectories(targetDir); + } + + // 移动文件 + Files.move(sourcePath, targetPath, StandardCopyOption.REPLACE_EXISTING); + } + + /** + * 删除文件 + * + * @param filePath 文件路径 + * @return 是否删除成功 + * @throws IOException IO异常 + */ + public static boolean deleteFile(String filePath) throws IOException { + return Files.deleteIfExists(Paths.get(filePath)); + } + + /** + * 创建目录 + * + * @param dirPath 目录路径 + * @return 是否创建成功 + * @throws IOException IO异常 + */ + public static boolean createDirectory(String dirPath) throws IOException { + Path path = Paths.get(dirPath); + if (Files.exists(path)) { + return Files.isDirectory(path); + } + Files.createDirectories(path); + return true; + } + + /** + * 删除目录(包括目录下的所有文件) + * + * @param dirPath 目录路径 + * @return 是否删除成功 + * @throws IOException IO异常 + */ + public static boolean deleteDirectory(String dirPath) throws IOException { + Path path = Paths.get(dirPath); + if (!Files.exists(path)) { + return true; + } + + // 递归删除目录内容 + Files.walkFileTree(path, new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + Files.delete(file); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { + Files.delete(dir); + return FileVisitResult.CONTINUE; + } + }); + + return true; + } + + /** + * 获取文件大小 + * + * @param filePath 文件路径 + * @return 文件大小(字节) + * @throws IOException IO异常 + */ + public static long getFileSize(String filePath) throws IOException { + return Files.size(Paths.get(filePath)); + } + + /** + * 获取文件扩展名 + * + * @param fileName 文件名 + * @return 文件扩展名(小写) + */ + public static String getFileExtension(String fileName) { + if (fileName == null || fileName.lastIndexOf('.') == -1) { + return ""; + } + return fileName.substring(fileName.lastIndexOf('.') + 1).toLowerCase(); + } + + /** + * 生成唯一文件名 + * + * @param originalFileName 原始文件名 + * @return 唯一文件名 + */ + public static String generateUniqueFileName(String originalFileName) { + String extension = getFileExtension(originalFileName); + String timestamp = new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date()); + String uuid = UUID.randomUUID().toString().replaceAll("-|", ""); + + if (extension.isEmpty()) { + return timestamp + "_" + uuid; + } else { + return timestamp + "_" + uuid + "." + extension; + } + } + + /** + * 列出目录下的所有文件 + * + * @param dirPath 目录路径 + * @return 文件路径列表 + * @throws IOException IO异常 + */ + public static List listFiles(String dirPath) throws IOException { + List filePaths = new ArrayList<>(); + Path path = Paths.get(dirPath); + + if (Files.exists(path) && Files.isDirectory(path)) { + Files.walkFileTree(path, new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + filePaths.add(file.toString()); + return FileVisitResult.CONTINUE; + } + }); + } + + return filePaths; + } + + /** + * 获取文件的MIME类型 + * + * @param filePath 文件路径 + * @return MIME类型 + * @throws IOException IO异常 + */ + public static String getMimeType(String filePath) throws IOException { + return Files.probeContentType(Paths.get(filePath)); + } + + /** + * 检查文件是否存在 + * + * @param filePath 文件路径 + * @return 是否存在 + */ + public static boolean fileExists(String filePath) { + return Files.exists(Paths.get(filePath)); + } + + /** + * 清理临时文件 + * + * @param dirPath 临时目录路径 + * @param daysAgo 清理多少天前的文件 + * @return 清理的文件数量 + * @throws IOException IO异常 + */ + public static int cleanTempFiles(String dirPath, int daysAgo) throws IOException { + final int[] deletedCount = {0}; + Path path = Paths.get(dirPath); + + if (!Files.exists(path) || !Files.isDirectory(path)) { + return deletedCount[0]; + } + + long cutoffTime = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(daysAgo); + + Files.walkFileTree(path, new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + try { + // 检查文件的最后修改时间 + if (Files.getLastModifiedTime(file).toMillis() < cutoffTime) { + Files.delete(file); + deletedCount[0]++; + } + } catch (IOException e) { + logger.error("删除临时文件失败: {}", file, e); + } + return FileVisitResult.CONTINUE; + } + }); + + return deletedCount[0]; + } + + /** + * 创建临时文件 + * + * @param prefix 文件前缀 + * @param suffix 文件后缀 + * @param tempDir 临时目录 + * @return 临时文件路径 + * @throws IOException IO异常 + */ + public static String createTempFile(String prefix, String suffix, String tempDir) throws IOException { + // 确保临时目录存在 + createDirectory(tempDir); + + // 创建临时文件 + File tempFile = File.createTempFile(prefix, suffix, new File(tempDir)); + return tempFile.getAbsolutePath(); + } +} \ No newline at end of file diff --git a/src/main/java/com/example/demo/common/utils/XmlParserUtil.java b/src/main/java/com/example/demo/common/utils/XmlParserUtil.java new file mode 100644 index 0000000..92f0000 --- /dev/null +++ b/src/main/java/com/example/demo/common/utils/XmlParserUtil.java @@ -0,0 +1,382 @@ +package com.example.demo.common.utils; + +import cn.hutool.core.util.StrUtil; +import org.w3c.dom.*; +import org.xml.sax.InputSource; +import javax.xml.parsers.DocumentBuilderFactory; +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.*; + +/** + * XML解析工具类 + * 提供XML文件解析和转换为TXT文件的核心功能 + * + * @author 岳佳君 (2025年09月24日 23:09:02) + * @version 1.0.0 + */ +public class XmlParserUtil { + + /** + * 解析XML文件并转换为TXT文件 + * + * @param xmlFilePath XML文件路径 + * @param txtFilePath TXT文件路径 + * @throws Exception 解析过程中的异常 + */ + public static void parseXmlToTxt(String xmlFilePath, String txtFilePath) throws Exception { + // 读取XML文件并解析 + Document document = parseXmlFile(xmlFilePath); + + // 创建TXT文件 + createTxtFile(document, txtFilePath); + } + + /** + * 解析XML文件为Document对象 + * + * @param xmlFilePath XML文件路径 + * @return Document对象 + * @throws Exception 解析异常 + */ + public static Document parseXmlFile(String xmlFilePath) throws Exception { + // 确保文件存在 + File file = new File(xmlFilePath); + if (!file.exists()) { + throw new FileNotFoundException("XML文件不存在: " + xmlFilePath); + } + + // 创建DocumentBuilder并解析文件 + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(true); + factory.setIgnoringElementContentWhitespace(true); + + try (FileInputStream fis = new FileInputStream(file)) { + InputSource is = new InputSource(new InputStreamReader(fis, StandardCharsets.UTF_8)); + return factory.newDocumentBuilder().parse(is); + } + } + + /** + * 将Document对象转换为TXT文件 + * + * @param document XML文档对象 + * @param txtFilePath TXT文件路径 + * @throws IOException IO异常 + */ + public static void createTxtFile(Document document, String txtFilePath) throws IOException { + // 确保输出目录存在 + Path outputPath = Paths.get(txtFilePath).getParent(); + if (outputPath != null && !Files.exists(outputPath)) { + Files.createDirectories(outputPath); + } + + // 写入TXT文件 + try (BufferedWriter writer = Files.newBufferedWriter(Paths.get(txtFilePath), StandardCharsets.UTF_8)) { + // 处理根元素 + Element rootElement = document.getDocumentElement(); + processElement(rootElement, writer, 0); + } + } + + /** + * 递归处理XML元素并写入TXT文件 + * 将中文XML标签转换为特定的TXT格式 + * + * @param element 当前XML元素 + * @param writer 输出写入器 + * @param indentLevel 缩进级别 + * @throws IOException IO异常 + */ + private static void processElement(Element element, BufferedWriter writer, int indentLevel) throws IOException { + String tagName = element.getTagName(); + String indent = " ".repeat(Math.max(0, indentLevel)); + + // 根据中文标签名映射到对应的TXT格式 + switch (tagName) { + case "任务": + processTaskElement(element, writer, indentLevel); + break; + case "任务信息": + processTaskInfoElement(element, writer, indentLevel); + break; + case "平台": + processPlatformElement(element, writer, indentLevel); + break; + case "行动": + processActionElement(element, writer, indentLevel); + break; + default: + processGenericElement(element, writer, indentLevel); + break; + } + } + + /** + * 处理任务元素 + */ + private static void processTaskElement(Element element, BufferedWriter writer, int indentLevel) throws IOException { + String indent = " ".repeat(Math.max(0, indentLevel)); + + // 获取任务行动信息 + String taskAction = getElementTextByTagName(element, "任务行动信息"); + if (StrUtil.isNotBlank(taskAction)) { + writer.write(indent + "task " + taskAction + " # 任务信息"); + writer.newLine(); + } + + // 处理子元素 + processChildElements(element, writer, indentLevel + 1); + + writer.write(indent + "end_task"); + writer.newLine(); + } + + /** + * 处理任务信息元素 + */ + private static void processTaskInfoElement(Element element, BufferedWriter writer, int indentLevel) throws IOException { + String indent = " ".repeat(Math.max(0, indentLevel)); + + // 处理指令有效期(拆分为开始和结束时间) + String instructionPeriod = getElementTextByTagName(element, "指令有效期"); + if (StrUtil.isNotBlank(instructionPeriod)) { + String[] timeRange = instructionPeriod.split("~"); + if (timeRange.length == 2) { + writer.write(indent + "start_time " + timeRange[0].trim()); + writer.newLine(); + writer.write(indent + "end_time " + timeRange[1].trim()); + writer.newLine(); + } + } + + // 处理其他子元素 + processChildElements(element, writer, indentLevel); + } + + /** + * 处理平台元素 + */ + private static void processPlatformElement(Element element, BufferedWriter writer, int indentLevel) throws IOException { + String indent = " ".repeat(Math.max(0, indentLevel)); + + writer.write(indent + "platform # 平台信息"); + writer.newLine(); + + // 处理平台子元素 + String platformName = getElementTextByTagName(element, "平台名称"); + String machineType = getElementTextByTagName(element, "机型"); + String numOfCpus = getElementTextByTagName(element, "架数"); + String platformId = getElementTextByTagName(element, "平台识别码"); + String country = getElementTextByTagName(element, "国家"); + + if (StrUtil.isNotBlank(platformName)) { + writer.write(indent + " platform_name " + platformName); + writer.newLine(); + } + if (StrUtil.isNotBlank(machineType)) { + writer.write(indent + " machine_type " + machineType); + writer.newLine(); + } + if (StrUtil.isNotBlank(numOfCpus)) { + writer.write(indent + " num_of_cpus " + numOfCpus); + writer.newLine(); + } + if (StrUtil.isNotBlank(platformId)) { + writer.write(indent + " platform_id " + platformId); + writer.newLine(); + } + if (StrUtil.isNotBlank(country)) { + writer.write(indent + " country " + country); + writer.newLine(); + } + + // 处理行动子元素 + processChildElements(element, writer, indentLevel + 1); + + writer.write(indent + "end_platform"); + writer.newLine(); + } + + /** + * 处理行动元素 + */ + private static void processActionElement(Element element, BufferedWriter writer, int indentLevel) throws IOException { + String indent = " ".repeat(Math.max(0, indentLevel)); + + writer.write(indent + "action"); + writer.newLine(); + + // 处理行动属性 + if (element.hasAttributes()) { + NamedNodeMap attributes = element.getAttributes(); + for (int i = 0; i < attributes.getLength(); i++) { + Attr attr = (Attr) attributes.item(i); + if ("label".equals(attr.getName())) { + writer.write(indent + " action_name " + attr.getValue()); + writer.newLine(); + } + } + } + + // 处理航线名称 + String routeName = getElementTextByTagName(element, "航线名称"); + if (StrUtil.isNotBlank(routeName)) { + writer.write(indent + " route_name " + routeName); + writer.newLine(); + } + + writer.write(indent + "end_action"); + writer.newLine(); + } + + /** + * 处理通用元素 + */ + private static void processGenericElement(Element element, BufferedWriter writer, int indentLevel) throws IOException { + String tagName = element.getTagName(); + String indent = " ".repeat(Math.max(0, indentLevel)); + + // 将中文标签名转换为英文(简单映射) + String englishTag = convertChineseTagToEnglish(tagName); + + if (StrUtil.isNotBlank(englishTag)) { + String textContent = getElementText(element); + if (StrUtil.isNotBlank(textContent)) { + writer.write(indent + englishTag + " " + textContent); + writer.newLine(); + } + } + } + + /** + * 处理子元素 + */ + private static void processChildElements(Element parent, BufferedWriter writer, int indentLevel) throws IOException { + NodeList children = parent.getChildNodes(); + for (int i = 0; i < children.getLength(); i++) { + if (children.item(i) instanceof Element) { + processElement((Element) children.item(i), writer, indentLevel); + } + } + } + + /** + * 根据标签名获取元素文本内容 + */ + private static String getElementTextByTagName(Element parent, String tagName) { + NodeList elements = parent.getElementsByTagName(tagName); + if (elements.getLength() > 0) { + Element element = (Element) elements.item(0); + return getElementText(element); + } + return ""; + } + + /** + * 将中文标签转换为英文标签 + */ + private static String convertChineseTagToEnglish(String chineseTag) { + // 简单的中文到英文映射 + switch (chineseTag) { + case "指令确认信息": return "confirm_info"; + case "覆盖的指令文档": return "document_version"; + default: return chineseTag; + } + } + + /** + * 从XML字符串解析为Document对象 + * + * @param xmlContent XML字符串内容 + * @return Document对象 + * @throws Exception 解析异常 + */ + public static Document parseXmlString(String xmlContent) throws Exception { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(true); + + try (InputStream inputStream = new ByteArrayInputStream(xmlContent.getBytes(StandardCharsets.UTF_8))) { + InputSource is = new InputSource(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); + return factory.newDocumentBuilder().parse(is); + } + } + + /** + * 获取XML元素的文本内容 + * + * @param element XML元素 + * @return 文本内容 + */ + public static String getElementText(Element element) { + if (element == null) { + return ""; + } + NodeList childNodes = element.getChildNodes(); + StringBuilder textContent = new StringBuilder(); + + for (int i = 0; i < childNodes.getLength(); i++) { + Node node = childNodes.item(i); + if (node.getNodeType() == Node.TEXT_NODE) { + textContent.append(node.getNodeValue()); + } + } + + return textContent.toString().trim(); + } + + /** + * 获取指定路径的XML元素 + * + * @param document XML文档对象 + * @param xpath 元素路径 + * @return 元素列表 + */ + public static List getElementsByPath(Document document, String xpath) { + List elements = new ArrayList<>(); + + // 简单实现XPath功能,实际项目中可使用JAXB或XPath API + String[] pathParts = xpath.split("/"); + if (pathParts.length == 0) { + return elements; + } + + // 查找根元素 + Element currentElement = document.getDocumentElement(); + if (!currentElement.getTagName().equals(pathParts[0])) { + return elements; + } + + // 递归查找子元素 + for (int i = 1; i < pathParts.length; i++) { + currentElement = findChildElement(currentElement, pathParts[i]); + if (currentElement == null) { + return elements; + } + } + + elements.add(currentElement); + return elements; + } + + /** + * 在父元素中查找指定名称的子元素 + * + * @param parent 父元素 + * @param tagName 子元素标签名 + * @return 子元素 + */ + private static Element findChildElement(Element parent, String tagName) { + NodeList childNodes = parent.getChildNodes(); + for (int i = 0; i < childNodes.getLength(); i++) { + Node node = childNodes.item(i); + if (node.getNodeType() == Node.ELEMENT_NODE && node.getNodeName().equals(tagName)) { + return (Element) node; + } + } + return null; + } +} \ No newline at end of file