parent
37fba96fc5
commit
f16d376674
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in new issue