文章出處
文章列表
一個很naive的代碼,用來做ftp的“主->從 下載,從->主 上傳”。ftp可不像mysql那樣還有log可以用,所以完全naive的做法:連到ftp server然后遞歸列出所有目錄,和維護的文件列表對比,有新增文件就下載到從服務器(其實是本地PC,囧);然后從服務器也列出目錄下文件列表,和維護的文件列表比較,有新增就上傳。維護一個文件列表,作用是:當主ftp server刪除文件的時候,從服務器不會上傳刪掉的文件。
使用python自帶的ftplib,感覺功能應該挺弱的,不過也沒怎么發現更強大的ftp的python庫。中文處理還是比較拙計,windows上的目錄和文件名字都要用name.decode('utf-8').encode('gbk')
這樣丑陋的方式處理才能正確使用,一個小問題是,如果從服務器新增文件名含有中文,上傳到主服務器后文件名亂碼,用的時候注意。
# coding: utf-8
# author: ChrisZZ, zchrissirhcz@gmail.com
# description: 做ftp同步
# 有本地主機A,和ftp服務器C,需要從A上傳圖片到C服務器
# 同步策略:增加中間變量B,維護一個文件列表。C上出現過的文件都存,C上刪除的則不刪除;A上新增的也存
# 形式化表示為:
# B = {}
# while (true) {
# B += C - B
# A += B - A
# B += A - B
# }
# 需要做的幾個模塊
# 1. 定義文件列表數據結構,用于A,B,C的表示和加法、減法
# 2. ftp連接
# 3. ftp獲取服務器上文件目錄結構
# 4. ftp復制文件
# 5. ftp緩沖區大小,重傳機制
# 編碼處理
# qq='喜歡你'
# charset_detect = chardet.detect(qq)
# cur_charset = charset_detect['encoding']
# print charset_detect
# #qq.decode(cur_charset)
# qq=qq.decode('utf-8')
# qq=qq.encode('gbk')
# print qq
# step1 ftp連接
from ftplib import FTP
import sys
import os
import string
import time
# import chardet
#reload(sys)
#sys_encode = sys.getfilesystemencoding()
#print '系統默認的編碼為', sys_encode
# print mystr.decode('utf-8').encode(type)
#reload(sys)
#sys.setdefaultencoding('utf8')
class Myfile(object):
def __init__(self, name, size, mtime):
self.name = name # 文件名字
self.mtime = mtime # 文件創建時間
self.is_dir = False # 是否為文件夾,默認為不是文件夾
#self.size = float(size) / (1024 * 1024) # 文件大小
size = float(size)
if size > 1024*1024:
self.size = str('%.2f'%(size / (1024*1024))) + 'MB'
elif size > 1024:
self.size = str('%.2f'%(size / 1024)) + 'KB'
else:
self.size = str(size) + 'Bytes'
@property
def is_file(self):
return not self.is_dir
@property
def dir_property(self):
if self.is_dir==True:
return 'dir'
return 'file'
def show(self):
print '[%s], [%s], [%s], [%s]' % (self.name, self.size, self.mtime, self.dir_property)
@property
def pack(self):
"""
將myfile對象封裝為一個字符串
:return:
"""
#return '[%s][%s][%s]'%(self.name, self.size, self.mtime)
return '[%s][%s]'%(self.name, self.size)
def ftpconnect():
ftp_server = 'host_ip'
username = 'name'
password = 'password'
ftp=FTP(ftp_server)
ftp.login(username, password)
#ftp.set_debuglevel(2) #打開調試級別2,顯示詳細信息
#ftp.connect(ftp_server,21) #連接
#ftp.login(username,password) #登錄,如果匿名登錄則用空串代替即可
return ftp
def str_codec_std(mystr):
"""
將字符串轉換編碼,使得本地打印輸出是正常的!
具體講,就是從ascii編碼轉化為utf-8編碼
:param mystr:需要轉化編碼的字符串
:return:轉化好編碼的字符串
"""
return mystr.decode('gbk').encode('utf8')
def filter_dir_list(mystr_list):
"""
過濾目錄列表,包括轉換編碼,以及去除'.'和'..'目錄,然后創建Myfile對象
:param mystr_list:
:return:
"""
res = []
for mystr in mystr_list:
mystr = str_codec_std(mystr)
# print "mystr is :%s" % mystr
file_info = string.split(mystr, maxsplit=8) # 一共有9列,第9列是文件名。文件名可能有空格,所以要指定maxsplit. 從0開始計數,name是第8列
# 形如['-rw-r--r--', '1', '48', 'apache', '1018596', 'Dec', '1', '20:08', 'RPN_BF.pdf']
name = file_info[8]
# print 'name = ', name
if name == '.' or name == '..':
continue
size = file_info[4]
mtime = '%s-%s-%s' % (file_info[5], file_info[6], file_info[7])
myfile = Myfile(name=name, size=size, mtime=mtime)
dir_info = file_info[0]
if dir_info[0] == 'd':
myfile.is_dir = True
res.append(myfile)
return res
def list_local_dir(local_dir):
"""
列出本地目錄下的內容,并將local_dir換成root路徑'/',并轉為unix風格路徑
:param local_dir:本地路徑,比如'g:/code/ftp_sync'
:return:文件列表,是local_dir路徑下遞歸找出的文件
"""
#local_dir = 'g:/code/ftp_sync'
g=os.walk(local_dir)
res = []
for path,d,filelist in g:
path = path.replace("\\", "/")
if path[-1]!='/':
path = path + '/'
prefix = path[len(local_dir):]
for filename in filelist:
filename = filename
name = os.path.join('/', prefix, filename)
size = os.path.getsize(os.path.join(path, filename))
size = float(size)
if size > 1024 * 1024:
size = str('%.2f' % (size / (1024 * 1024))) + 'MB'
elif size > 1024:
size = str('%.2f' % (size / 1024)) + 'KB'
else:
size = str(size) + 'Bytes'
mtime = os.path.getmtime(os.path.join(path, filename))
mtime = time.strftime('%b-%d-%H:%M', time.localtime(mtime))
#item = '/'+prefix+'/'+filename
record = '[%s][%s]' % (name, size)
record = record.decode('gbk').encode('utf-8')
res.append(record)
return res
def list_server_dir(server_dir=None):
"""
獲取ftp服務器上,指定目錄下遞歸列出的文件列表
:param server_dir: ftp服務器上的目錄,比如'/photo'
:return:文件列表,是server_dir路徑下遞歸找出的文件
"""
#sys.setdefaultencoding('utf8')
#sys_encode = sys.getfilesystemencoding()
# 先連接服務器
ftp = ftpconnect()
# 列出指定目錄下所有文件
server_file_list = []
fuck_callback = lambda x: (server_file_list.append(x))
ftp.retrlines('LIST', fuck_callback)
# 生成C
server_file_items = filter_dir_list(server_file_list)
#for server_file in server_file_items:
# server_file.show()
#for server_file in server_file_list:
# #print server_file.decode('gbk').encode('utf-8')
# print string.split(server_file,maxsplit=8)
# 關閉連接
ftp.quit()
def get_C(ftp, target_dir=None):
C = []
if target_dir is not None:
ftp.cwd(target_dir) # change working directory to target_dir
server_file_list = []
fuck_callback = lambda x: (server_file_list.append(x))
ftp.retrlines('LIST', fuck_callback)
server_file_items = filter_dir_list(server_file_list)
for item in server_file_items:
if item.is_dir:
sub_C = get_C(ftp, item.name)
# sub_C = ['/'+item.name+'/'+cc.name for cc in sub_C]
for cc in sub_C:
cc.name = '/'+item.name+cc.name
C.extend(sub_C)
else:
item.name = '/' + item.name
C.append(item)
return C
def old_main():
#if __name__ == '__main__':
#dir_content = list_local_dir('g:/code/ftp_sync')
#for dir_line in dir_content:
# print dir_line
# list_server_dir()
# 生成A
local_sync_dir = 'g:/code/ftp_sync/sycn'
A = list_local_dir(local_sync_dir)
# 生成C TODO:連接失敗的處理
ftp = ftpconnect()
server_file_list = []
fuck_callback = lambda x: (server_file_list.append(x))
ftp.retrlines('LIST', fuck_callback)
server_file_items = filter_dir_list(server_file_list)
# C = [item.pack for item in server_file_items if item.is_file]
C = get_C(ftp)
C = [cc.name for cc in C]
for cc in C:
print cc
def list_diff(a, b):
"""
差集操作A-B
:param a:
:param b:
:return:
"""
ret = []
for i in a:
if i not in b:
ret.append(i)
return ret
#if __name__ == '__main__':
def future_main():
# 設定B
B = []
# 設定C
ftp = ftpconnect()
C = get_C(ftp)
#ftp.quit()
C = [cc.pack for cc in C]
# 設定A
local_sync_dir = 'g:/code/ftp_sync/sync'
A = list_local_dir(local_sync_dir)
print '====== Begin本地文件列表(A)'
for aa in A:
print aa
print '====== End本地文件列表(A)'
# B += C - B
delta = list_diff(C, B)
B.extend(delta)
print '====== Begin維護文件列表(B)'
for bb in B:
print bb
print '====== End維護文件列表(B)'
#print '====before===='
#for aa in A:
# print aa
#print len(A)
# A += B - A
delta = list_diff(B, A)
print '====== Begin服務器上新增文件列表(B-A):'
for d in delta:
print d
# TODO: 把delta里的文件都下載下來
bufsize = 20000 * 1024
for filename in delta:
filename = filename.split(']')[0][1:]
filename = filename.decode('utf-8').encode('gbk')
#print 'filename is : ', filename
LocalFile = local_sync_dir + filename
print '將服務器上的文件%s保存為本地文件%s' % (filename.decode('gbk').encode('utf-8'), LocalFile.decode('gbk').encode('utf-8'))
server_dir = filename[0:filename.rindex('/')+1]
#print 'server_dir:' , server_dir
ftp.cwd(server_dir)
short_filename = filename[filename.rindex('/')+1:]
#print 'short_filename is ', short_filename
#LocalFile = string.replace(LocalFile, '/', '\\')
#print 'item is:', LocalFile
#print 'local_sync_dir is ', local_sync_dir
#local_sync_dir = string.replace(local_sync_dir, '/', '\\')
local_dir = LocalFile[0:LocalFile.rindex('/')+1]
print 'local_dir is ', local_dir
if(not os.path.exists(local_dir)):
print '創建目錄%s' % local_dir
os.makedirs(local_dir)
#LocalFile = LocalFile.decode('utf-8').encode('gbk')
fh = open(LocalFile, 'wb')
ftp.retrbinary('RETR '+short_filename, fh.write, bufsize)
#ftp.storbinary('STOR %s' % os.path.basename(short_filename), fh.write, bufsize)
#ftp.storbinary('STOR %s'%short_filename, fh, bufsize)
fh.close()
A.extend(delta)
print "==== ^_^Begin下載完畢,現在本地文件列表如下"
for aa in A:
print aa
print "==== ^_^End下載完畢,現在本地文件列表如下"
# B += A - B
delta = list_diff(A, B)
# TODO: 把delta里面的文件都上傳上去
for filename in delta:
filename = filename.split(']')[0][1:]
#filename = filename.decode('utf-8').encode('gbk')
LocalFile = local_sync_dir + filename
#LocalFile = LocalFile.decode('utf-8').encode('gbk')
print '將本地文件%s上傳到為服務器文件%s' % (LocalFile.decode('gbk').encode('utf-8'), filename.decode('gbk').encode('utf-8'))
server_dir = filename[0:filename.rindex('/') + 1]
ftp.cwd(server_dir)
# todo:檢查服務器上路徑server_dir是否存在,若不存在則創建
short_filename = filename[filename.rindex('/') + 1:]
#local_dir = LocalFile[0:LocalFile.rindex('/') + 1]
#print 'local_dir is ', local_dir
#if (not os.path.exists(local_dir)):
# print '創建目錄%s' % local_dir
# os.makedirs(local_dir)
LocalFile = LocalFile.decode('utf-8').encode('gbk')
fh = open(LocalFile, 'rb')
ftp.storbinary('STOR ' + LocalFile, fh, bufsize)
fh.close()
B.extend(delta)
print "==== !!任務結束!!"
ftp.quit()
def download_example():
ftp = ftpconnect()
local_sync_dir = 'g:/code/ftp_sync/sync'
#filename = 'test.php'
filename = '反卷積iccv2011.pdf'
filename = filename.decode('utf-8').encode('gbk')
LocalFile = local_sync_dir + '/' + filename
#ftp.set_pasv(0)
fh = open(LocalFile, 'wb')
bufsize = 1000*1024
ftp.retrbinary('RETR %s'%filename, fh.write, bufsize)
fh.close()
ftp.close()
def upload_example():
ftp = ftpconnect()
local_sync_dir = 'g:/code/ftp_sync/sync'
filename = 'test你.php'
import chardet
#print chardet.detect(filename)
#filename = filename.decode('gbk').encode('utf-8')
LocalFile = local_sync_dir + '/' + filename
print 'LocalFile is : %s' % LocalFile
LocalFile = LocalFile.decode('utf-8').encode('gbk')
# ftp.set_pasv(0)
fh = open(LocalFile, 'rb')
bufsize = 1000 * 1024
ftp.storbinary('STOR %s' % filename, fh, bufsize)
fh.close()
ftp.close()
if __name__ == '__main__':
#download_example()
#upload_example()
future_main()
文章列表
全站熱搜