配置谷歌云maven:
<!-- Google Drive API -->
<dependency>
<groupId>com.google.api-client</groupId>
<artifactId>google-api-client</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>com.google.oauth-client</groupId>
<artifactId>google-oauth-client-jetty</artifactId>
<version>1.33.3</version>
</dependency>
<dependency>
<groupId>com.google.apis</groupId>
<artifactId>google-api-services-drive</artifactId>
<version>v3-rev20230815-2.0.0</version>
</dependency>
MySQL备份工具类:
@Component
@Slf4j
public class MySQLBackupUtil {
@Value("${spring.datasource.url}")
private String dbUrl;
@Value("${spring.datasource.username}")
private String dbUsername;
private String host;
private int port;
private String database;
@Value("${spring.datasource.password}")
private String dbPassword;
// 自定义的文件夹存放位置
@Value("${spring.datasource.base-file-path}")
private String baseFilePath;
@PostConstruct
private void initDbArgs() {
try {
URI uri = new URI(dbUrl.substring(5));
host = uri.getHost();
port = uri.getPort();
String path = uri.getPath();
database = path.contains("?") ? path.split("\\?")[0] : path.substring(1);
java.io.File file = new java.io.File(baseFilePath);
if (!file.exists()) {
log.info("文件夹创建:{}", file.mkdirs() ? "成功" : "失败");
}
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}
public File backupDatabase() {
try {
String backupFileName = "/backup-" + new Date().getTime() + ".sql";
String backupFilePath = baseFilePath + backupFileName;
File backupFolder = new File(backupFilePath);
// mysqldump好像默认不会创建文件所以自己创建文件
if (!backupFolder.exists()) {
log.info("文件创建:{}", backupFolder.createNewFile() ? "成功" : "失败");
}
String command = "mysqldump -h " + host + " -P " + port + " -u "
+ dbUsername + " -p" + dbPassword + " " + database + " > "
+ backupFilePath;
ProcessBuilder processBuilder = new ProcessBuilder("bash", "-c", command);
Process process = processBuilder.start();
log.info("开始执行command");
// 捕获错误输出
InputStream errorStream = process.getErrorStream();
BufferedReader errorReader = new BufferedReader(new InputStreamReader(errorStream));
String errorLine;
StringBuilder errorOutput = new StringBuilder();
while ((errorLine = errorReader.readLine()) != null) {
errorOutput.append(errorLine).append("\n");
}
int exitCode = process.waitFor();
log.info("执行command结束");
if (exitCode == 0) {
log.info("数据库备份成功!");
return backupFolder;
} else {
throw new RuntimeException("数据库备份失败!错误输出:" + errorOutput);
}
} catch (Exception e) {
throw new RuntimeException("数据库备份发生错误:{}" + e.getMessage());
}
}
}
上传谷歌云:
考虑到后期增加备份到其他位置没有在service中写死
@Getter
@AllArgsConstructor
public enum BackupEnum {
GOOGLE(1, "谷歌云备份"),
;
private final Integer type;
private final String desc;
}
public abstract class AbstractBackupHandler {
@PostConstruct
private void init() {
BackupHandlerFactory.register(getBackupEnumTypeEnum().getType(), this);
}
abstract BackupEnum getBackupEnumTypeEnum();
public abstract String backup();
public abstract void deleteBackup();
}
public class BackupHandlerFactory {
private static final Map<Integer, AbstractBackupHandler> BACKUP_HANDLER_MAP = new HashMap<>();
public static void register(Integer code, AbstractBackupHandler strategy) {
BACKUP_HANDLER_MAP.put(code, strategy);
}
public static AbstractBackupHandler getStrategy(Integer code) {
return BACKUP_HANDLER_MAP.get(code);
}
}
@Component
@Slf4j
public class GoogleBackupHandler extends AbstractBackupHandler {
@Value("${google.drive.folderId}")
private String googleDriveFolderId;
@Value("${google.drive.credentialsPath}")
private String googleDriveCredentialsPath;
private static Drive drive = null;
@Autowired
private ResourceLoader resourceLoader;
@Autowired
private MySQLBackupUtil backupUtil;
@PostConstruct
private void initDbArgs() {
try {
drive = getDrive();
} catch (IOException | GeneralSecurityException e) {
throw new RuntimeException(e);
}
}
@Override
BackupEnum getBackupEnumTypeEnum() {
return BackupEnum.GOOGLE;
}
@Override
public String backup() {
try {
java.io.File backupFolder = backupUtil.backupDatabase();
log.info("开始上传谷歌云盘");
File fileMetadata = new File();
fileMetadata.setName(backupFolder.getName());
fileMetadata.setParents(Collections.singletonList(googleDriveFolderId));
FileContent mediaContent = new FileContent("application/sql", backupFolder);
File uploadedFile = drive.files().create(fileMetadata, mediaContent)
.setFields("id")
.execute();
log.info("文件删除:{}", backupFolder.delete() ? "成功" : "失败");
String uploadedFileId = uploadedFile.getId();
log.info("备份已上传到谷歌云盘,文件ID:{}", uploadedFileId);
return uploadedFileId;
} catch (Exception e) {
throw new RuntimeException("删除7天前的备份发生错误:" + e.getMessage());
}
}
@Override
public void deleteBackup() {
log.info("开始删除7天前的备份");
try {
ZoneId zoneId = ZoneId.of("UTC");
LocalDateTime time = LocalDateTime.now(zoneId).minusDays(7);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("''yyyy-MM-dd'T'HH:mm:ss.SSS'Z'''");
String cutoff = time.format(formatter);
FileList fileList = drive.files().list()
.setFields("files(id, name)")
.setQ("modifiedTime < " + cutoff)
.execute();
for (File file : fileList.getFiles()) {
drive.files().delete(file.getId()).execute();
}
log.info("删除7天前的备份完成");
} catch (Exception e) {
throw new RuntimeException("删除7天前的备份发生错误:" + e.getMessage());
}
}
private Drive getDrive() throws IOException, GeneralSecurityException {
Resource resource = resourceLoader.getResource(googleDriveCredentialsPath);
InputStream inputStream = resource.getInputStream();
GoogleCredential credential = GoogleCredential
.fromStream(inputStream)
.createScoped(DriveScopes.all());
return new Drive.Builder(GoogleNetHttpTransport.newTrustedTransport(), GsonFactory.getDefaultInstance(), credential)
.setApplicationName("MysqlBackup")
.build();
}
}
@Service
public class BackupService {
public void deleteBackup(Integer code){
AbstractBackupHandler backupHandler = BackupHandlerFactory.getStrategy(code);
if (backupHandler == null){
throw new RuntimeException("错误的删除备份方式");
}
backupHandler.deleteBackup();
}
public String backup(Integer code){
AbstractBackupHandler backupHandler = BackupHandlerFactory.getStrategy(code);
if (backupHandler == null){
throw new RuntimeException("该备份方式不存在");
}
return backupHandler.backup();
}
}