一個基於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