一個基於一個基於aliyun-oss-python-sdk的代碼同步工具

一個基於aliyun-oss-python-sdk的代碼同步工具,可以指定同步文件的類型。(水平很爛,請輕噴 (:3 」∠) ) 要使用的話需要租一個阿里雲oss2雲存儲服務,很便宜,9rmb/year 因爲流量是走的開發者接口,所以流量免費,用來同步一些小文件很划算 我是用來同步代碼文件的,當然其他類型的文件也可以。 交互方式採用命令行。 注意: 使用前須將 endpoint accessKeyId accessKeySecret 替換爲自己的,以及需要在在oss2控制檯上新建一個Bucket 然後將Bucket_name替換爲自己的Bucket_name auth=oss2.Auth(accessKeyId,accessKeySecret) bucket=oss2.Bucket(auth,endpoint,Bucket_name)

掃描,更新文件都採用多線程的方式,注意線程不能開太多,會發生網絡阻塞,且容易造成雲端服務器無響應。

個人水平有限,有問題歡迎來交流。

oss2_sync_tool_v3.py.py

import os,shutil
import oss2
import logging
import my_utils as utils
import threading
#配置信息,注意替換爲自己的
endpoint=''
accessKeyId=''
accessKeySecret=''
#local_path='C:/Users/pc/workspace/java workspace/'
local_path_list=['C:/Users/pc/workspace/java workspace/','C:/Users/pc/workspace/sublime/']#因爲分割符的位置在下面是硬性指定的,所以最好保證列表中的目錄是等深度的,不然就得單獨處理,這裏就對應45,125行,129,130,137,138行的索引5,深度改變了,索引也要改
temp_path='C:/Users/pc/oss2_sync_cache/'#緩存路徑
cloud_path='my_src_code/'#雲端文件夾

include_suffix=['c','cpp','java','py','txt','xml','properties','h','hpp']#待同步文件類型可以自行指定,進一步的,也可以對其他的文件信息進行個性化定製,如文件大小等


auth=oss2.Auth(accessKeyId,accessKeySecret)
bucket=oss2.Bucket(auth,endpoint,'xxxx')#這裏的xxxx改成自己的bucket_name


src_file_list=[]
temp_file_list=[]
lock=threading.Lock()
def generate_cache(src_file_list,temp_file_list):
    for srcEntry,tempEntry in zip(src_file_list,temp_file_list):
        if not os.path.exists(tempEntry):#先看文件存在否
            lock.acquire()
            if not os.path.exists('/'.join(tempEntry.split('/')[:-1])):#再看目錄存在否
                os.makedirs('/'.join(tempEntry.split('/')[:-1]))
            lock.release()
            with open(tempEntry,'w+') as f:#先創建空文件(因爲不存在)
                f.close()

        if utils.newer(srcEntry,tempEntry) or os.path.getsize(tempEntry)==0:#如果源文件較新或目標文件爲空文件(可能是之前剛創建的),才更新
            shutil.copy2(srcEntry,tempEntry)#copy2()會連帶着源文件狀態(時間戳)一起復制,copy+copystat

#掃描源目錄,並生成臨時存儲信息到temp_file_list中,然後根據temp_file_list來更新臨時文件
def src2temp(temp_path,thread_number=16):
    #1.掃描源目錄,並將符合的文件路徑存儲到src_file_list中
    for entry_list in local_path_list:
        utils.scan(entry_list,src_file_list,include_suffix)
    for entry in src_file_list:
        temp_file_list.append(temp_path+'/'.join(entry.split('/')[5:]))
        #print(temp_path+'/'.join(entry.split('/')[5:]))

    assert(len(src_file_list)==len(temp_file_list))
    #更新臨時文件,引入多線程處理
    tmp_s=[]
    tmp_d=[]
    threads=[]
    for i in range(thread_number):
        tmp_s.append(list())
        tmp_d.append(list())
    for i in range(len(src_file_list)):
        tmp_s[i%thread_number].append(src_file_list[i].replace('//','/'))
        tmp_d[i%thread_number].append(temp_file_list[i].replace('//','/'))
    for i in range(thread_number):
        threads.append(threading.Thread(target=generate_cache,args=(tmp_s[i],tmp_d[i],)))
    for t in threads:
        t.setDaemon(True)
        t.start()
    for t in threads:
        t.join()

def update_file(temp_file_list):
    for entry in temp_file_list:
        #拆分本地目錄名和文件名,生成雲端路徑
        utils.update_file(bucket,
                    '/'.join(entry.split('/')[:-1])+'/',
                    entry.split('/')[-1],
                    (cloud_path+'/'.join(entry.split('/')[4:-1])+'/').replace('//','/'),
                    #//會導致雲端生成空目錄,所以替換
                    entry.split('/')[-1])

        #print('/'.join(entry.split('/')[:-1])+'/'+entry.split('/')[-1])
        #print((cloud_path+'/'.join(entry.split('/')[5:-1])+'/').replace('//','/')+entry.split('/')[-1])
        

#將臨時文件同步更新到雲端,不直接從源文件同步到雲端,做了一個隔離,對源文件只有讀權限,對臨時文件纔有讀寫權限
def temp2cloud(endpoint,accessKeyId,accessKeySecret,thread_number=16):
    #掃描源目錄,並生成臨時存儲信息到temp_file_list中,然後根據temp_file_list來更新臨時文件
    src2temp(temp_path)
    if not bucket.object_exists(cloud_path):
        bucket.put_object(cloud_path)

    tmp_c=[]
    threads=[]
    total_size=0
    for i in range(thread_number):
        tmp_c.append(list())
    for i in range(len(temp_file_list)):
        tmp_c[i%thread_number].append(temp_file_list[i].replace('//','/'))
        total_size+=os.path.getsize(temp_file_list[i].replace('//','/'))
    for i in range(thread_number):
        threads.append(threading.Thread(target=update_file,args=(tmp_c[i],)))#特別注意要有逗號
    for t in threads:
        t.setDaemon(True)
        t.start()
    for t in threads:
        t.join()
    
    print('scan file number: {0}\ntotal size: {0} kB'.format(len(temp_file_list),total_size/1000))



def print_info():
    print('*************** oss2_sync_tool_v3 **********************')
    print('* input ls:  list the files that need to be updated.   *')
    print('* input ls -u:  update list after execute ls.          *')
    print('* input update:  update all files.                     *')
    print('* input cfg-s:  configure include_suffix.              *')
    print('* input cfg-n:  configure oss2-bucket-name.            *')
    print('* input restore:  restore to the original state.       *')
    print('* input clear:  clear all temp files.                  *')
    print('* input show-info:  set up display information or not. *')
    print('* input help:  show the help information.              *')
    print('* input q:  exit the program.                          *')
    print('*************************************** --xycode *******')

ls_update_list=[]#待更新列表
def ls_part(ls_list):
    for entry in ls_list:#ls_list相當於之前的src_file_list,所以下面是[5:]
        temp_cloud_path=(cloud_path+'/'.join(entry.split('/')[5:-1])+'/').replace('//','/')+entry.split('/')[-1]
        #print(temp_cloud_path)
        if not bucket.object_exists(temp_cloud_path):#雲端不存在的話
            print(entry)
            generate_cache([entry],[temp_path+'/'.join(entry.split('/')[5:])])#先生成緩存
            ls_update_list.append(temp_path+'/'.join(entry.split('/')[5:]))
            continue
        statinfo=os.stat(entry)
        local_last_modified_time=int(statinfo.st_mtime)-28800#統一轉換爲以秒爲單位,北京時間-8小時=GMT
        cloud_last_modified_time=utils.date_to_num(bucket.get_object(temp_cloud_path))
        if local_last_modified_time>cloud_last_modified_time:#本地文件較新
            print(entry)
            shutil.copy2(entry,temp_path+'/'.join(entry.split('/')[5:]))
            ls_update_list.append(temp_path+'/'.join(entry.split('/')[5:]))


def ls(thread_number=24):
    ls_list=[]
    for entry_list in local_path_list:
        utils.scan(entry_list,ls_list,include_suffix)
    tmp_l=[]
    threads=[]
    for i in range(thread_number):
        tmp_l.append(list())
    for i in range(len(ls_list)):#分配任務
        tmp_l[i%thread_number].append(ls_list[i].replace('//','/'))
    for i in range(thread_number):
        threads.append(threading.Thread(target=ls_part,args=(tmp_l[i],)))#特別注意要有逗號
    for t in threads:
        t.setDaemon(True)
        t.start()
    for t in threads:
        t.join()

def clear():
    str=input('clear all temp file?(y/n)\n')
    if str in ['Y','y']:
        if os.path.exists(temp_path):
            shutil.rmtree(temp_path)
        os.makedirs(temp_path)
    else:
        return

store_include_suffix=[]
def cfg_suffix():
    global include_suffix #這句必須有,否則報錯
    global store_include_suffix
    store_include_suffix=include_suffix.copy()
    include_suffix.clear()
    s=input('please input suffix-name,separated by space:\n')
    include_suffix=s.split(' ')

'''
以後可以考慮用命令行設置
endpoint
accessKeyId
accessKeySecret
指定bucket name
以及用命令行恢復
這裏只是恢復include_suffix
'''
def restore():
    global include_suffix#注意
    global store_include_suffix#注意
    include_suffix=store_include_suffix.copy()


def interact(show_info=True):
    if show_info: print_info()
    command=input('please input a legal commmad:\n')
    if command=='ls':
        ls()
        if show_info:
            interact(True)
        else:
            interact(False)
    elif command=='ls -u':
        if ls_update_list==[]:
            print("ls_update_list is empty,no need to update.")
            print("please check for updates to execute command \'ls\' firstly.")
        else:
            update_file(ls_update_list)
        ls_update_list.clear()
        if show_info:
            interact(True)
        else:
            interact(False)
    elif command=='cfg-s':
        cfg_suffix()
        if show_info:
            interact(True)
        else:
            interact(False)
    elif command=='cfg-n':
        global bucket
        oss2_bucket_name=input('please input oss2-bucket-name:\n(legal name: [\'xycode1\',\'xycode2\'])\n')
        if oss2_bucket_name in ['xycode1','xycode2']:
            bucket=oss2.Bucket(auth,endpoint,oss2_bucket_name)
        else:
            print('illegal oss2-bucket-name!')
        if show_info:
            interact(True)
        else:
            interact(False)
    elif command=='restore':
        restore()
        if show_info:
            interact(True)
        else:
            interact(False)
    elif command=='update':
        src_file_list.clear()
        temp_file_list.clear()
        if not os.path.exists(temp_path):
            os.makedirs(temp_path)
        #src2temp(temp_path)
        temp2cloud(endpoint,accessKeyId,accessKeySecret)
        if show_info:
            interact(True)
        else:
            interact(False)
    elif command=='clear':
        clear()
        if show_info:
            interact(True)
        else:
            interact(False)
    elif command=='help':
        print_info()
        if show_info:
            interact(True)
        else:
            interact(False)
    elif command=='show-info':
        s=input('show?(y/n)\n')
        if s in ['Y','y']:
            interact(True)
        else:
            interact(False)
    elif command=='q':
        return
    else:
        print('incorrect command,please input again.')
        if show_info:
            interact(True)
        else:
            interact(False)


if __name__ == '__main__':
    # 設置日誌等級爲CRITICAL
    log_file_path = "log.log"
    oss2.set_file_logger(log_file_path, 'oss2', logging.CRITICAL)
    interact()


my_utils.py

import os,shutil,re
import oss2
import time
def date_to_num(GetObjectResult):
	result=' '.join(GetObjectResult.headers['Last-Modified'].split(' ')[1:-1])
	date_result=time.strptime(result,'%d %b %Y %H:%M:%S')
	date_num=time.mktime(date_result)
	return int(date_num)

#將本地文件同步到雲端
def update_file(bucket,local_file_path,local_filename,cloud_file_path,cloud_filename):
	statinfo=os.stat(local_file_path+local_filename)
	#注意時區的轉換
	# local_last_modified_time=time.mktime(time.localtime(statinfo.st_mtime))-28800#統一轉換爲以秒爲單位,北京時間-8小時=GMT
	local_last_modified_time=int(statinfo.st_mtime)-28800#統一轉換爲以秒爲單位,北京時間-8小時=GMT
	with open(oss2.to_unicode(local_file_path+local_filename),'rb') as f:
		if bucket.object_exists(cloud_file_path+cloud_filename):#雲端已經存在該文件
			cloud_last_modified_time=date_to_num(bucket.get_object(cloud_file_path+cloud_filename))
			# print(local_last_modified_time,cloud_last_modified_time)
			if local_last_modified_time>cloud_last_modified_time:
				#如果本地的文件較新才更新到雲端
				print('update '+local_filename+'.')
				bucket.put_object(cloud_file_path+cloud_filename,f)#將本地文件更新到雲端cloud_file_path+cloud_filename
			else:
				print(cloud_filename+' already up-to-date.')
		else:
			print(cloud_filename+' does not exist,start upload...')
			bucket.put_object(cloud_file_path+cloud_filename,f)#將本地文件上傳到雲端cloud_file_path+cloud_filename

#判斷源文件與臨時文件哪個較新
def newer(srcEntry,destEntry):
    statinfo=os.stat(srcEntry)
    src_last_modified_time=int(statinfo.st_mtime)
    statinfo=os.stat(destEntry)
    dest_last_modified_time=int(statinfo.st_mtime)
    return src_last_modified_time>dest_last_modified_time

#掃描目錄的函數,並將符合的文件路徑存儲到src_file_list中
def scan(path,src_file_list,include_suffix):
    if not os.path.exists(path):
        return
    if os.path.isdir(path):
       for entry in os.listdir(path):
           scan(path+'/'+entry,src_file_list,include_suffix)
    else:
        if path.split('.')[-1] in include_suffix:
            src_file_list.append(path)
            #print(path)

詳細代碼見我的github主頁:https://github.com/xycodec/oss2_sync_tool_public

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章