From f16d376674ab9411757aba66320eec248eb01519 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Fri, 10 Jan 2025 13:41:34 +0800 Subject: [PATCH] 'commit' --- .../YunXiaoTools/Utils/BackupMysqlUtil.java | 61 +----- .../YunXiaoTools/Utils/CommonUtil.java | 59 ++++++ .../YunXiaoTools/Utils/DatabaseRestore.java | 185 ++++++++++++++++++ 3 files changed, 248 insertions(+), 57 deletions(-) create mode 100644 YunXiaoTools/src/main/java/com/dsideal/YunXiaoTools/Utils/CommonUtil.java create mode 100644 YunXiaoTools/src/main/java/com/dsideal/YunXiaoTools/Utils/DatabaseRestore.java diff --git a/YunXiaoTools/src/main/java/com/dsideal/YunXiaoTools/Utils/BackupMysqlUtil.java b/YunXiaoTools/src/main/java/com/dsideal/YunXiaoTools/Utils/BackupMysqlUtil.java index 9d1f4f22..0ebdd4eb 100644 --- a/YunXiaoTools/src/main/java/com/dsideal/YunXiaoTools/Utils/BackupMysqlUtil.java +++ b/YunXiaoTools/src/main/java/com/dsideal/YunXiaoTools/Utils/BackupMysqlUtil.java @@ -7,8 +7,6 @@ import com.jfinal.kit.PropKit; import java.io.*; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; -import java.util.regex.Matcher; -import java.util.regex.Pattern; public class BackupMysqlUtil { //数据库连接串 @@ -22,15 +20,13 @@ public class BackupMysqlUtil { * 执行MySQL备份 */ public String backup() { - PropKit.clear(); - PropKit.use("application.properties"); jdbcUrl = PropKit.get("read.jdbcUrl"); user = PropKit.get("read.user"); password = PropKit.get("read.password"); // 获取配置 - String host = getHostFromJdbcUrl(jdbcUrl); - String port = getPortFromJdbcUrl(jdbcUrl); - String database = getDatabaseFromJdbcUrl(jdbcUrl); + String host = CommonUtil.getHostFromJdbcUrl(jdbcUrl); + String port = CommonUtil.getPortFromJdbcUrl(jdbcUrl); + String database = CommonUtil.getDatabaseFromJdbcUrl(jdbcUrl); // 生成备份文件名 String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")); String backupFileName = timestamp + ".sql"; @@ -40,7 +36,7 @@ public class BackupMysqlUtil { try { // 获取mysqldump路径 - String mysqldumpPath = getMySQLDumpPath(); + String mysqldumpPath = CommonUtil.getMySQLDumpPath(); // 构建命令(不包含密码) ProcessBuilder pb = new ProcessBuilder( mysqldumpPath, @@ -87,55 +83,6 @@ public class BackupMysqlUtil { } } - /** - * 获取mysqldump路径 - */ - private String getMySQLDumpPath() { - try { - String resourcePath = PropKit.get("mysqldump_path"); - // 处理路径中的空格和特殊字符 - if (resourcePath.startsWith("/")) { - resourcePath = resourcePath.substring(1); - } - // URL解码 - return java.net.URLDecoder.decode(resourcePath, "UTF-8"); - } catch (Exception e) { - throw new RuntimeException("获取mysqldump路径失败", e); - } - } - - /** - * 从JDBC URL中提取主机地址 - */ - private String getHostFromJdbcUrl(String jdbcUrl) { - String[] parts = jdbcUrl.split("://")[1].split(":"); - return parts[0]; - } - - /** - * 从JDBC URL中提取端口号 - */ - private String getPortFromJdbcUrl(String jdbcUrl) { - String[] parts = jdbcUrl.split("://")[1].split(":"); - return parts[1].split("/")[0]; - } - - /** - * 从JDBC URL中提取数据库名 - */ - private String getDatabaseFromJdbcUrl(String jdbcUrl) { - try { - // 使用正则表达式匹配数据库名 - Pattern pattern = Pattern.compile("jdbc:mysql://[^/]+/(\\w+)\\??.*"); - Matcher matcher = pattern.matcher(jdbcUrl); - if (matcher.find()) { - return matcher.group(1); - } - throw new RuntimeException("无法从JDBC URL中提取数据库名: " + jdbcUrl); - } catch (Exception e) { - throw new RuntimeException("解析数据库名失败: " + jdbcUrl, e); - } - } public String doAction() { String sourceFile = backup(); diff --git a/YunXiaoTools/src/main/java/com/dsideal/YunXiaoTools/Utils/CommonUtil.java b/YunXiaoTools/src/main/java/com/dsideal/YunXiaoTools/Utils/CommonUtil.java new file mode 100644 index 00000000..500663f0 --- /dev/null +++ b/YunXiaoTools/src/main/java/com/dsideal/YunXiaoTools/Utils/CommonUtil.java @@ -0,0 +1,59 @@ +package com.dsideal.YunXiaoTools.Utils; + +import com.jfinal.kit.PropKit; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class CommonUtil { + + /** + * 获取mysqldump路径 + */ + public static String getMySQLDumpPath() { + try { + String resourcePath = PropKit.get("mysqldump_path"); + // 处理路径中的空格和特殊字符 + if (resourcePath.startsWith("/")) { + resourcePath = resourcePath.substring(1); + } + // URL解码 + return java.net.URLDecoder.decode(resourcePath, "UTF-8"); + } catch (Exception e) { + throw new RuntimeException("获取mysqldump路径失败", e); + } + } + + /** + * 从JDBC URL中提取主机地址 + */ + public static String getHostFromJdbcUrl(String jdbcUrl) { + String[] parts = jdbcUrl.split("://")[1].split(":"); + return parts[0]; + } + + /** + * 从JDBC URL中提取端口号 + */ + public static String getPortFromJdbcUrl(String jdbcUrl) { + String[] parts = jdbcUrl.split("://")[1].split(":"); + return parts[1].split("/")[0]; + } + + /** + * 从JDBC URL中提取数据库名 + */ + public static String getDatabaseFromJdbcUrl(String jdbcUrl) { + try { + // 使用正则表达式匹配数据库名 + Pattern pattern = Pattern.compile("jdbc:mysql://[^/]+/(\\w+)\\??.*"); + Matcher matcher = pattern.matcher(jdbcUrl); + if (matcher.find()) { + return matcher.group(1); + } + throw new RuntimeException("无法从JDBC URL中提取数据库名: " + jdbcUrl); + } catch (Exception e) { + throw new RuntimeException("解析数据库名失败: " + jdbcUrl, e); + } + } +} diff --git a/YunXiaoTools/src/main/java/com/dsideal/YunXiaoTools/Utils/DatabaseRestore.java b/YunXiaoTools/src/main/java/com/dsideal/YunXiaoTools/Utils/DatabaseRestore.java new file mode 100644 index 00000000..bbb6e65f --- /dev/null +++ b/YunXiaoTools/src/main/java/com/dsideal/YunXiaoTools/Utils/DatabaseRestore.java @@ -0,0 +1,185 @@ +package com.dsideal.YunXiaoTools.Utils; + +import com.jfinal.kit.PropKit; +import com.obs.services.ObsClient; +import java.io.*; +import java.util.zip.*; + +public class DatabaseRestore { + private final String obsEndpoint; + private final String obsAccessKey; + private final String obsSecretKey; + private final String obsBucket; + + // 目标数据库配置 + private final String dbHost; + private final String dbPort; + private final String dbUser; + private final String dbPassword; + private final String dbName; + + public DatabaseRestore() { + // 从配置文件读取配置 + this.obsEndpoint = PropKit.get("obs.endPoint"); + this.obsAccessKey = PropKit.get("obs.accessKey"); + this.obsSecretKey = PropKit.get("obs.secretKey"); + this.obsBucket = PropKit.get("obs.bucketName"); + + // 目标数据库配置 + this.dbHost = CommonUtil.getHostFromJdbcUrl(PropKit.get("write.jdbcUrl")); + this.dbPort = CommonUtil.getPortFromJdbcUrl(PropKit.get("write.jdbcUrl")); + this.dbUser = PropKit.get("write.user"); + this.dbPassword = PropKit.get("write.password"); + this.dbName = CommonUtil.getDatabaseFromJdbcUrl(PropKit.get("write.jdbcUrl")); + } + + /** + * 执行完整的还原流程 + * @param obsKey OBS上的文件路径 + */ + public void restore(String obsKey) { + String tempDir = System.getProperty("java.io.tmpdir"); + String zipFile = tempDir + File.separator + new File(obsKey).getName(); + String sqlFile = null; + + try { + // 1. 从OBS下载ZIP文件 + downloadFromObs(obsKey, zipFile); + System.out.println("文件下载完成: " + zipFile); + + // 2. 解压ZIP文件 + sqlFile = unzipFile(zipFile); + System.out.println("文件解压完成: " + sqlFile); + + // 3. 还原数据库 + restoreDatabase(sqlFile); + System.out.println("数据库还原完成"); + + } catch (Exception e) { + throw new RuntimeException("还原过程失败: " + e.getMessage(), e); + } finally { + // 清理临时文件 + if (zipFile != null) { + new File(zipFile).delete(); + } + if (sqlFile != null) { + new File(sqlFile).delete(); + } + } + } + + /** + * 从OBS下载文件 + */ + private void downloadFromObs(String obsKey, String localFile) { + try (ObsClient obsClient = new ObsClient(obsAccessKey, obsSecretKey, obsEndpoint)) { + obsClient.getObject(obsBucket, obsKey, String.valueOf(new File(localFile))); + } catch (Exception e) { + throw new RuntimeException("从OBS下载文件失败: " + e.getMessage(), e); + } + } + + /** + * 解压ZIP文件 + */ + private String unzipFile(String zipFile) throws IOException { + String extractedFile = null; + + try (ZipInputStream zis = new ZipInputStream(new FileInputStream(zipFile))) { + ZipEntry zipEntry = zis.getNextEntry(); + if (zipEntry != null) { + extractedFile = new File(zipFile).getParent() + File.separator + zipEntry.getName(); + + try (BufferedOutputStream bos = new BufferedOutputStream( + new FileOutputStream(extractedFile))) { + byte[] buffer = new byte[8192]; + int len; + while ((len = zis.read(buffer)) > 0) { + bos.write(buffer, 0, len); + } + } + } + } + + if (extractedFile == null) { + throw new RuntimeException("ZIP文件中没有找到SQL文件"); + } + + return extractedFile; + } + + /** + * 还原数据库 + */ + private void restoreDatabase(String sqlFile) { + try { + // 获取mysql命令路径 + String mysqlPath = getMySQLPath(); + + // 构建还原命令 + ProcessBuilder pb = new ProcessBuilder( + mysqlPath, + "-h" + dbHost, + "-P" + dbPort, + "-u" + dbUser, + "--ssl-mode=DISABLED", + dbName + ); + + // 设置环境变量传递密码 + pb.environment().put("MYSQL_PWD", dbPassword); + + // 执行还原命令 + Process process = pb.start(); + + // 将SQL文件内容写入mysql进程的输入流 + try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(sqlFile)); + BufferedOutputStream bos = new BufferedOutputStream(process.getOutputStream())) { + + byte[] buffer = new byte[8192]; + int len; + while ((len = bis.read(buffer)) > 0) { + bos.write(buffer, 0, len); + } + } + + // 读取错误输出 + StringBuilder errorOutput = new StringBuilder(); + try (BufferedReader reader = new BufferedReader( + new InputStreamReader(process.getErrorStream()))) { + String line; + while ((line = reader.readLine()) != null) { + if (!line.contains("WARNING")) { + errorOutput.append(line).append("\n"); + System.out.println("还原进度: " + line); + } + } + } + + // 等待命令执行完成 + int exitCode = process.waitFor(); + if (exitCode != 0) { + throw new RuntimeException("数据库还原失败:\n" + errorOutput.toString()); + } + + } catch (Exception e) { + throw new RuntimeException("还原数据库失败: " + e.getMessage(), e); + } + } + + /** + * 获取mysql命令路径 + */ + private String getMySQLPath() { + try { + String resourcePath = getClass().getClassLoader() + .getResource("mysql.exe").getPath(); + if (resourcePath.startsWith("/")) { + resourcePath = resourcePath.substring(1); + } + return java.net.URLDecoder.decode(resourcePath, "UTF-8"); + } catch (Exception e) { + throw new RuntimeException("获取mysql路径失败", e); + } + } +} \ No newline at end of file