通过python全量备份或增量备份文件夹中所有文件

基于《python实现对文件的全量、增量备份 - goodTOgreat - 博客园》代码修改而成的新代码,新代码能够实现基于python全量备份或增量备份文件夹中所有文件。

# 用于实现对某些要求文件夹的全量备份和增量备份
import os
import shutil
import time
import hashlib
import pickle as p


# 将文件转换为md5文件
def md5check(fname):
    m = hashlib.md5()
    with open(fname) as fobj:
        while True:
            data = fobj.read(4096)
            if not data:
                break
            m.update(data.encode())
    return m.hexdigest()

# 全量备份,每次备份都是备份完整的文件夹所有内容
def full_backup(src_dir_list, dst_dir_total, selected_type, no_selected_dir=[]):
    time_stamp = time.strftime('%Y-%m-%d-%H-%M-%S')
    md5dict = {}                                                 # 字典,存储每个文件的md5文件
    md5file = os.path.join(dst_dir_total, "md5.data")
    for i, src_dir in enumerate(src_dir_list):                   # !!分别备份列表中的每个文件夹
        base_dir = src_dir.rsplit("/")[-1]                       # 当前文件夹的名称
        back_name = "full_%s/%d_%s" % (time_stamp, i, base_dir)  # 当前文件夹在备份目录下的名称
        dst_dir = os.path.join(dst_dir_total, back_name)         # 当前文件夹要备份到的最终目的目录
        if os.path.exists(dst_dir): shutil.rmtree(dst_dir)       # 所以src_dir是要备份的文件夹的地址
        os.makedirs(dst_dir)                                     # dst_dir是要备份的文件夹的目的地址
        print("=="*80)
        print("backup '%s' to '%s'" % (src_dir, dst_dir))
        for root, dirs, files in os.walk(src_dir):               # !!递归遍历src_dir的每一级目录结构
            if root.split("/")[-1] in no_selected_dir: continue  # 剔除不要的文件夹,只是名称不是地址
            print("--"*70)
            print("backup '%s' to '%s'" % (root, root.replace(src_dir, dst_dir)))
            for dir in dirs:                                     # !!构建当前一级的文件夹结构
                dst_sub_dir = os.path.join(root.replace(src_dir, dst_dir), dir)
                if dir not in no_selected_dir:
                    os.makedirs(dst_sub_dir)
                    print("bulid dir '%s'" % dst_sub_dir)
                else:
                    print("-d-d-d- give up dir '%s' !!!" % dst_sub_dir)
            for file in files:                                   # !!构建当前一级的文件结构
                src_file = os.path.join(root, file)         
                if file.split(".")[-1] in selected_type:         # 剔除不要的文件类型
                    src_flie_dst_dir = root.replace(src_dir, dst_dir)  # shutil.copy(A,B) A是文件,B是文件夹
                    shutil.copy(src_file, src_flie_dst_dir)      # shutil.copyfile(A,B) A是文件,B是文件
                    md5dict[src_file] = [md5check(src_file), ""] # 记录每个要备份文件的md5文件, shutil.copytree(A,B) A是文件夹,B是文件夹
                    print("copy '%s' to '%s'" % (src_file, src_flie_dst_dir))
                else:
                    print("-f-f-f- give up file '%s' !!!" % src_file)

    if os.path.exists(md5file):
        with open(md5file, 'wb') as f0:  # w,只写模式【不可读;不存在则创建;存在则清空内容;】
            p.dump(md5dict, f0)          # r ,只读模式【默认】
    else:
        with open(md5file, 'xb') as f1:  # x, 只写模式【不可读;不存在则创建,存在则报错】
            p.dump(md5dict,f1)           # a, 追加模式【不可读;   不存在则创建;存在则只追加内容;】

# 增量备份,每次备份都是仅仅备份与上一次相比增加或修改的文件,这里如果存在文件删除,不做任何记录,调用后md5文件更新为此时完整目录的记录
def incr_backup(src_dir_list, dst_dir_total, selected_type, no_selected_dir=[]):
    time_stamp = time.strftime('%Y-%m-%d-%H-%M-%S')
    md5new = {}                                                  # 字典,存储每个文件的md5文件
    md5file = os.path.join(dst_dir_total, "md5.data")
    for i, src_dir in enumerate(src_dir_list):                   # !!分别备份列表中的每个文件夹
        base_dir = src_dir.rsplit("/")[-1]
        back_name = "incr_%s/%d_%s" % (time_stamp, i, base_dir)
        dst_dir = os.path.join(dst_dir_total, back_name)
        if os.path.exists(dst_dir): shutil.rmtree(dst_dir)
        for root, dirs, files in os.walk(src_dir):               # !!递归遍历src_dir的每一级目录结构
            if root.split("/")[-1] in no_selected_dir: continue
            for file in files:                                   # !!遍历当前一级的文件结构,无需建立当前文件夹结构,因为可能文件夹没啥变化
                src_file = os.path.join(root, file)
                if file.split(".")[-1] in selected_type:
                    src_flie_dst_dir = root.replace(src_dir, dst_dir)
                    md5new[src_file] = [md5check(src_file), src_flie_dst_dir]  # 记录当前时刻总目录里每个要做备份检查的文件的md5文件

    if os.path.exists(md5file):           # 打开之前的md5文件记录
        with open(md5file,'rb') as fobj:
            md5old = p.load(fobj)
    else:
        md5old = {}

    with open(md5file, 'wb') as fobj:     # 存储当前的md5文件记录
        p.dump(md5new, fobj)

    for key in md5new:                    # 检查当前总目录结构相比之前总目录结构发生了什么变化,如果有增加或修改文件,则记录下来,删除文件不做记录
        if key in md5old:
            if md5old[key][0] == md5new[key][0]:
                continue                  # 如果文件没有任何变化则不备份
        key_flie_dst_dir = md5new[key][1]
        if not os.path.exists(key_flie_dst_dir):
            os.makedirs(key_flie_dst_dir)
            print("bulid '%s'" % key_flie_dst_dir)
        shutil.copy(key, key_flie_dst_dir)
        print("copy '%s' to '%s'" % (key, key_flie_dst_dir))


if __name__ == '__main__':
    src_dir_list = ["/nfs/cache-902-2/xiawenze/VehicleClassification"]  # 需要备份的文件夹(子文件夹默认都一起备份),可以是多个文件夹
    dst_dir_total = "/nfs/cache-902-2/xiawenze/backup"                  # 备份目的地
    selected_type = ["py", "md"]                                        # 需要备份的文件后缀
    no_selected_dir = []                                                # 不需要备份的文件夹名称,这里只能是某文件夹名称,而不是路径,例如所有名为tmp的文件夹不备份
    if time.strftime("%a") == "Fri":                                    # 每周五进行全量备份,即完整备份当前的所有内容
        full_backup(src_dir_list, dst_dir_total, selected_type, no_selected_dir)
    else:                                                               # 其它时间仅仅备份相比上次备份新增加或被修改的文件,相比上次备份删除的文件不管
        incr_backup(src_dir_list, dst_dir_total, selected_type, no_selected_dir)

猜你喜欢

转载自blog.csdn.net/BIT_Legend/article/details/122851980