一个很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()

ftp同步代码的更多相关文章

  1. 微信开发之SVN提交代码与FTP同步到apache的根目录

    SVN是协同开发的,版本控制器,就是几个人同时开发,可以提交代码到SVN服务器,这样就可以协同开发,一般是早上上班首先更新下代码,然后自己修改代码 工作一天之后,修改代码之后,下班之前,更新代码,然后 ...

  2. 后盾网lavarel视频项目---phpstorm 配置ftp, 自动更新同步代码

    后盾网lavarel视频项目---phpstorm 配置ftp, 自动更新同步代码 一.总结 一句话总结: 1.在phpstorm中设置:路径Tools/Deployment/Configuratio ...

  3. [.NET] 怎样使用 async & await 一步步将同步代码转换为异步编程

    怎样使用 async & await 一步步将同步代码转换为异步编程 [博主]反骨仔 [出处]http://www.cnblogs.com/liqingwen/p/6079707.html  ...

  4. 如何在 Visual Studio 中使用 Git 同步代码到 CodePlex

    开源社区不管在国内还是国外都很火热,微软也曾因为没有开源而倍受指责,但是随着 .Net framework.ASP.Net MVC等框架的逐渐开源,也让大家看到了微软开源的步伐.CodePlex 则是 ...

  5. TODO:Github的使用技巧之同步代码

    TODO:Github的使用技巧之同步代码 GitHub 是一个面向开源及私有软件项目的托管平台,因为只支持 Git 作为唯一的版本库格式进行托管,故名 GitHub. GitHub 于 2008 年 ...

  6. About 静态代码块,普通代码块,同步代码块,构造代码块和构造函数的纳闷

    构造函数用于给对象进行初始化,是给与之对应的对象进行初始化,它具有针对性,函数中的一种.特点:1:该函数的名称和所在类的名称相同.2:不需要定义返回值类型.3:该函数没有具体的返回值.记住:所有对象创 ...

  7. 0037 Java学习笔记-多线程-同步代码块、同步方法、同步锁

    什么是同步 在上一篇0036 Java学习笔记-多线程-创建线程的三种方式示例代码中,实现Runnable创建多条线程,输出中的结果中会有错误,比如一张票卖了两次,有的票没卖的情况,因为线程对象被多条 ...

  8. 2016/9/25编写java实验报告时对synchronized(同步代码块)的一些感悟

    通过此次实验,明白了多线程的设置和启动.synchronized代码块的用法.线程的优先级使用方法.知道了那几类资源是线程共享的. 我现在理解的多线程是:实例化一个继承了Thread类或实现了Runn ...

  9. git 设置 key 到服务器,同步代码不需要输入用户名和密码

    1  ssh-keygen -t rsa 2  vim ~/.ssh/id_rsa.pub 3. 添加到git 服务器,这样同步代码就不需要输入密码

随机推荐

  1. win10 下visual studio 2015 在调试模式下不能跟踪源文件

    win10 下visual studio 2015 在调试模式下不能跟踪源文件,只要一调试就会关闭(隐藏)打开的文档,非常不方便.经过一番折腾,发现是配置的问题. 如果安装多个版本的VS,请删除对应版 ...

  2. 很强大的HTML+CSS+JS面试题(附带答案)

    一.单项选择(165题) 1.HTML是什么意思? A)高级文本语言 B)超文本标记语言 C)扩展标记语言 D)图形化标记语言 2.浏览器针对于HTML文档起到了什么作用? A)浏览器用于创建HTML ...

  3. 2D banner

    1.这是我第一次发博客咯!看到本文章后不喜勿喷,有什么需要改进的地方请多多指教! 2.今天和大家分享一下2D banner,代码如下,注释都有.因为本地测试和上传到博客环境不太一样,样式变化比较大,样 ...

  4. UIView的layoutSubviews和drawRect方法何时调用

    首先两个方法都是异步执行.layoutSubviews方便数据计算,drawRect方便视图重绘. layoutSubviews在以下情况下会被调用: 1.init初始化不会触发layoutSubvi ...

  5. 【代码笔记】iOS-正方形转换

    一,工程图. 二,代码. RootViewControlle.m //点击任何处,页面翻转 -(void)touchesBegan:(NSSet *)touches withEvent:(UIEven ...

  6. Dagger2 (二) 进阶篇

    一.作用域Scope 之前了解RoboGuice的时候,我们知道它默认给我们提供了几个注解,ContextSingleton和Singleton,但是Dagger2更为灵活,只有javax包中提供的S ...

  7. ubuntu 16 安装django nginx uWSGI

    参考 https://www.digitalocean.com/community/tutorials/how-to-serve-django-applications-with-uwsgi-and- ...

  8. 使用gulp解决外部编辑器修改Eclipse文件延迟更新的问题

    本人前端用惯了Hbuilder,修改了eclipse项目中的文件后,由于是外部编辑器修改过的,eclipse不会自动部署更新,一般按F5刷新项目,或者在 preferences > genera ...

  9. postgres创建表的过程以及部分源码分析

    背景:修改pg内核,在创建表时,表名不能和当前的用户名同名. 首先我们知道DefineRelation此函数是最终创建表结构的函数,最主要的参数是CreateStmt这个结构,该结构如下 typede ...

  10. mysql limit分页查询优化写法

    在mysql中进行分页查询时,一般会使用limit查询,而且通常查询中都会使用orderby排 序.但是在表数据量比较大的时候,例如查询语句片段limit 10000, 20,数据库会读取10020条 ...