demo背景:由于同事说Excel表格在写着东西又突然被还原到之前的内容了,后面写的内容全部不见了。刚好最近自己在学习java,为此就用java写了一个小程序,这个小程序的功能比较简陋,纯属个人练习java某些语法点所用,具体如下:
1.可以指定需要备份的文件、和备份到哪个文件目录以及指定备份时间间隔;
2.只备份最新的文件,如果曾经已经存在过(也就是说文件被未知原因或人为恢复至前面的内容),则不备份,同时给予提示;
不足:
1.只能备份保存后的文件;
2.不支持大文件;
3.不支持备份目录;
4.不支持自动清理过期的备份文件;
实现原理:
1.通过生成文件的MD5值识别,并当做key添加进HashMap集合里面;
2.按一定时间段获取文件的MD5值,看是否改变,如果改变,并且HashMap里面的key没有相应的MD5值,则添加进集合。同时,把对应的文件备份。反之,不备份。
============================================================
1.获取文件MD5类(网上找的,出处暂时无从考证)
package md5offile;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
/** * Md5校验工具类 * @author Fengwx */
public class MD5Util {
public static void main(String[] args){
System.out.println(getFileMD5(new File("D:/javaTest/c/c.txt")));
System.out.println(getFileMD5(new File("D:/javaTest/a/b/c/abc")));
}
private static final char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9','a', 'b', 'c', 'd', 'e', 'f'};
/** * Get MD5 of a file (lower case)
* @return empty string if I/O error when get MD5
*/
public static String getFileMD5(File file) {
FileInputStream in = null;
try {
in = new FileInputStream(file);
FileChannel ch = in.getChannel();
return MD5(ch.map(FileChannel.MapMode.READ_ONLY, 0, file.length()));
} catch (FileNotFoundException e) {
return "";
} catch (IOException e) {
return "";
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
// 关闭流产生的错误一般都可以忽略
}
}
}
}
/** * MD5校验字符串 * @param s String to be MD5
* * @return 'null' if cannot get MessageDigest
* */
private static String getStringMD5(String s) {
MessageDigest mdInst;
try {
// 获得MD5摘要算法的 MessageDigest 对象
mdInst = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return "";
}
byte[] btInput = s.getBytes();
// 使用指定的字节更新摘要
mdInst.update(btInput);
// 获得密文
byte[] md = mdInst.digest();
// 把密文转换成十六进制的字符串形式
int length = md.length;
char str[] = new char[length * 2];
int k = 0;
for (byte b : md) {
str[k++] = hexDigits[b >>> 4 & 0xf];
str[k++] = hexDigits[b & 0xf];
}
return new String(str);
}
private static String getSubStr(String str, int subNu, char replace) {
int length = str.length();
if (length > subNu) {
str = str.substring(length - subNu, length);
} else if (length < subNu) {
// NOTE: padding字符填充在字符串的右侧,和服务器的算法是一致的
str += createPaddingString(subNu - length, replace);
}
return str;
}
private static String createPaddingString(int n, char pad) {
if (n <= 0) {
return "";
}
char[] paddingArray = new char[n];
Arrays.fill(paddingArray, pad);
return new String(paddingArray);
}
/**
* * 计算MD5校验
* * @param buffer
* * @return 空串,如果无法获得 MessageDigest实例
* */
private static String MD5(ByteBuffer buffer) {
String s = "";
try {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(buffer);
byte tmp[] = md.digest();
// MD5 的计算结果是一个 128 位的长整数,
// 用字节表示就是 16 个字节
char str[] = new char[16 * 2];
// 每个字节用 16 进制表示的话,使用两个字符,
// 所以表示成 16 进制需要 32 个字符
int k = 0;
// 表示转换结果中对应的字符位置
for (int i = 0; i < 16; i++) {
// 从第一个字节开始,对 MD5 的每一个字节
// 转换成 16 进制字符的转换
byte byte0 = tmp[i];
// 取第 i 个字节
str[k++] = hexDigits[byte0 >>> 4 & 0xf];
// 取字节中高 4 位的数字转换, >>>,
// 逻辑右移,将符号位一起右移
str[k++] = hexDigits[byte0 & 0xf];
// 取字节中低 4 位的数字转换
}
s = new String(str);
// 换后的结果转换为字符串
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return s;
}
}
2.实现备份类
package autoBackup;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Random;
import java.util.Scanner;
import java.util.Timer;
import java.util.TimerTask;
import md5offile.*;
public class TimerBackupFile {
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner scan = new Scanner(System.in);
System.out.println("输入需要备份的源文件:");
String strPathSource = scan.nextLine();//输入需要备份的源目录。格式:D:\test.txt
System.out.println("输入需要备份到哪里的目标目录:");
String strPathTar = scan.nextLine();//输入需要备份到哪里的目标目录,必须存在的目录。格式:D:\javaTest\
Timer t = new Timer();
t.schedule(new BackupFile(strPathSource,strPathTar), 6000, 120000);//6秒后开始备份,每2分钟备份一次
System.out.println("当前时间:" + BackupFile.setDateFormat(System.currentTimeMillis()));
System.out.println("............");
}
}
class BackupFile extends TimerTask{
String strPathSource,strPathTar;
HashMap<String,String> hm = new HashMap<>();
public BackupFile(String strPathSource,String strPathTar){
this.strPathSource = strPathSource;
this.strPathTar = strPathTar;
}
public void backupFile(String strPathSource,String strPathTar){
if(!hm.keySet().contains(BackupFile.getFileMD5(strPathSource))){//如果文件内容不变,或者恢复到之前的内容,都不备份。
hm.put(BackupFile.getFileMD5(strPathSource), setDateFormat(System.currentTimeMillis()));//把文件的MD5值存入集合hm的key中
try {
BufferedInputStream bin = new BufferedInputStream(new FileInputStream(strPathSource));
BufferedOutputStream bou = new BufferedOutputStream(
new FileOutputStream(strPathTar + randomStr(10) + new File(strPathSource).getName()));//增加随机字符串,为的是防止文件名一样,备份失败
int i = 0;
while((i = bin.read())!=-1){
bou.write(i);
}
bin.close();
bou.close();
} catch (FileNotFoundException e) {
System.out.println("文件没有找到~!");
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(System.currentTimeMillis() + "文件备份成功!");
}else{
System.out.println("文件曾经存在过,不给予备份~请注意!");
}
}
//生成随机数字符串
public String randomStr(int len){
Random rd = new Random();
String temp = "";
for(int i=0;i<=len;i++){
int num = rd.nextInt(len);
temp += String.valueOf(num);
}
return temp;
}
//转化时间格式
public static String setDateFormat(long time){
Date dt = new Date(time);
DateFormat df = new SimpleDateFormat("YYYY年MM月DD日 hh时MM分ss秒");
return df.format(dt);
}
//生成文件的MD5值
public static String getFileMD5(String strPathSource){
return MD5Util.getFileMD5(new File(strPathSource));
}
public void run(){
backupFile(strPathSource,strPathTar);
}
}