前段时间公司要求给服务器上一个存放数据的文件弄一个备份,在网上找了很多软件,要么就是收费要么就是service系统不能用,干脆自己写一个,慢点就慢点吧。
准备:
开发环境:
Idea
jdk1.8
maven
在Idea中创建一个springboot的项目,我这边有需求mvc所以导入mvc的包;
pom文件:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.gjdw</groupId>
<artifactId>syncfolder</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>syncfolder</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
写一个实体类作为参数:
package com.gjdw.syncfolder.entity;
public class SyncFolder {
private Long id;
// 需要备份的文件夹路径
private String oldFolderPath;
// 备份到的文件夹
private String saveFolderPath;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getOldFolderPath() {
return oldFolderPath;
}
public void setOldFolderPath(String oldFolderPath) {
this.oldFolderPath = oldFolderPath;
}
public String getSaveFolderPath() {
return saveFolderPath;
}
public void setSaveFolderPath(String saveFolderPath) {
this.saveFolderPath = saveFolderPath;
}
}
编写工具链用来备份文件:
package com.gjdw.syncfolder.util;
import com.gjdw.syncfolder.entity.SyncFolder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.DigestUtils;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Vector;
/**
* @author Administrator
*/
public class FileSave {
// 将文件备份到的文件夹
private String savePath;
// 需要备份的文件夹
private String filePath;
// 复制的文件总数
private int fileNum = 0;
// 错误的文件总数
private int errorNum = 0;
// 记录所有线程
private List<Thread> threads = new ArrayList<Thread>();
private static final transient Logger log = LoggerFactory.getLogger(FileSave.class);
// method整合
public int copy(SyncFolder syncFolder) throws IOException, InterruptedException {
Vector<Thread> vector = new Vector<Thread>();
System.out.println();
String path = syncFolder.getOldFolderPath();
filePath = path;
savePath = syncFolder.getSaveFolderPath();
File file = new File(path);
if(file.isDirectory()){
File saveFile = new File(savePath);
if(!saveFile.isDirectory()){
log.info(savePath+"保存文件夹不存在!!!");
return -1;
}
log.info("开始备份"+path+"文件夹");
fileWritePath(file);
log.info("所有线程数: "+String.valueOf(threads.size()));
for (Thread t:threads) {
t.join();
System.out.println("等待:"+t.toString());
}
log.info("本次备份文件:" + fileNum + "个");
return fileNum;
}else{
log.info(path+"文件夹不存在!!!");
return -1;
}
}
// 1、获取该文件或文件夹下所有文件路径
// 2、如果是文件则备份到指定位置
// 3、如果是文件夹则循环遍历
public void fileWritePath(File source) throws IOException {
// 如果是文件夹迭代
if (source.isDirectory()) {
if (source.listFiles() != null && source.listFiles().length != 0)
for (File file : source.listFiles())
fileWritePath(file);
}
// 如果是文件,且不是该文件自身
if (source.isFile()
&& source.toString().indexOf("FileSave.class") == -1) {
//copyFile(source);
Thread thread = new Thread() {
@Override
public void run() {
try {
//对指定的文件进行复制操作
String newPath = savePath
+ source.getAbsolutePath().toString();
newPath = newPath.replace(new String(savePath + filePath.substring(3)).replace("/", "\\"), savePath);
newPath = newPath.replace(filePath, "");
//获取新文件所在路径
File oldFile = new File(newPath);
// 如果文件存在
if (oldFile.exists()) {
System.out.println(oldFile.getPath()+"文件存在");
System.out.println();
} else {
// 如果文件不存在
// 先创建该文件所在的文件夹
File file = new File(oldFile.toString().substring(0,
(newPath.lastIndexOf("\\"))));
// 同时创建多层文件夹
file.mkdirs();
System.out.println(oldFile.getPath()+"文件备份完成");
// 进行文件复制操作
FileInputStream infile = new FileInputStream(source.toString());
FileOutputStream outfile = new FileOutputStream(oldFile.toString(), true);
byte[] bb = new byte[1024];
int lenth = 0;
while ((lenth = infile.read(bb)) != -1) {
outfile.write(bb, 0, lenth);
}
outfile.flush();
outfile.close();
infile.close();
fileNum++;
System.out.println(fileNum);
// 对备份的文件数进行统计
}
} catch (IOException e) {
e.printStackTrace();
}
}
};
thread.start();
threads.add(thread);
}
}
// 字节流文件复制
// public void copy_sy4(String oldFile, String newFile) throws IOException {
// FileInputStream infile = new FileInputStream(oldFile);
// FileOutputStream outfile = new FileOutputStream(newFile, true);
// byte[] bb = new byte[1024];
// int lenth = 0;
// while ((lenth = infile.read(bb)) != -1) {
// outfile.write(bb, 0, lenth);
// }
// outfile.flush();
// outfile.close();
// infile.close();
// }
// 文件重名
// 只留一个备份文件
public void fileNewName(File file) {
String fileSuffix = new SimpleDateFormat("HH-mm-ss").format(new Date());
file.renameTo(new File(file.toString() + "~" + fileSuffix));
}
}
这里我们用到了多线程,可以提高备份的失效性,实测1g的数据26000个左右的文件在顺序执行的情况下需要30秒-40每秒之间,使用多线程只需7秒,具体情况看机器配置来决定,而且主线程等待备份数据的线程用的方法不是很好。
使用Scheduled用来做定时任务
package com.gjdw.syncfolder.scheduled;
import com.gjdw.syncfolder.entity.SyncFolder;
import com.gjdw.syncfolder.util.FileSave;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.scheduling.annotation.Scheduled;
import java.io.IOException;
/**
* 定时任务类,用来执行定时任务
* */
@Component
public class FileScheduled {
private static final transient Logger log = LoggerFactory.getLogger(FileScheduled.class);
@Scheduled(cron = "0 00 21 * * ?")
public void fixTimeExecution() {
log.info("*****************开始执行邮件定时任务********************");
FileSave f = new FileSave();
int fieNum = 0;
try {
SyncFolder syncFolder = new SyncFolder();
syncFolder.setOldFolderPath("D:\\kjzx");
syncFolder.setSaveFolderPath("H:\\kjzx_backup");
fieNum = f.copy(syncFolder);
log.info("*****************定时任务完成,此次备份文件"+fieNum+"个********************");
} catch (IOException | InterruptedException e) {
e.printStackTrace();
log.info("*****************定时任务失败***********************");
}
}
}
这样一个基本的文件夹备份程序基本写完了,但是注意这样只能执行定时任务,需要手动触发和多任务备份,需要在写上页面等代码,这里就不一一讲述,希望对你有所帮助。