ftp同步代码

简介: 一个很naive的代码,用来做ftp的“主->从 下载,从->主 上传”。ftp可不像mysql那样还有log可以用,所以完全naive的做法:连到ftp server然后递归列出所有目录,和维护的文件列表对比,有新增文件就下载到从服务器(其实是本地PC,囧);然后从服务器也列出目录下文件列表,和维护的文件列表比较,有新增就上传。

一个很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()
目录
相关文章
|
8月前
|
Java 数据安全/隐私保护
Java——通过Java代码连接ftp服务器
Java——通过Java代码连接ftp服务器
|
网络协议 Linux 网络安全
文件共享同步5种方式:NFS、NAS、rsync、scp、ftp
谈到文件同步,我们最直接的同步方式是采用rsync的同步软件,rsync同步可以保持server和client的强一致(server中的增删改都会同步client),但在实际场景中rsync可能并不能被采纳。
14108 0
|
8月前
|
Java
Java——通过Java代码从ftp服务器下载文件
Java——通过Java代码从ftp服务器下载文件
|
DataWorks 大数据
离线同步到ftp数据压缩后无法解压使用
离线同步到ftp数据压缩后无法解压使用
离线同步到ftp数据压缩后无法解压使用
如何在Webstorm/Phpstorm中设置连接FTP,并快速进行文件比较,上传下载,同步等操作
Phpstorm除了能直接打开localhost文件之外,还可以连接FTP,除了完成正常的数据传递任务之外,还可以进行本地文件与服务端文件的异同比较,同一文件自动匹配目录上传,下载,这些功能是平常IDE,FTP软件中少见的,而且是很耗工作时间的一个操作。
2103 0
|
开发者
用flashfxp做ftp镜像同步
简单说,用flashfxp建立上传或者下载队列,然后设定一个定时任务来处理这个队列,就能同步下载或上传制定的目录了。 本人遇到的需求只需要做同步上传,记录如下: 开发者PC ---> 国内win server --->法国ftp server A B C A是普通开发者,或者美工啥的,希望把本机上弄好的图片等文件传输到法国的ftp服务器上。
1213 0
|
IDE 开发工具 数据安全/隐私保护
如何在Webstorm/Phpstorm中设置连接FTP,并快速进行文件比较,上传下载,同步等操作
原文:如何在Webstorm/Phpstorm中设置连接FTP,并快速进行文件比较,上传下载,同步等操作 Phpstorm除了能直接打开localhost文件之外,还可以连接FTP,除了完成正常的数据传递任务之外,还可以进行本地文件与服务端文件的异同比较,同一文件自动匹配目录上传,下载,这些功能是平常IDE,FTP软件中少见的,而且是很耗工作时间的一个操作。
1590 0
|
XML Java 数据格式
java FTP 工具类 同步文件夹 下载文件..等操作(FtpHelper 需要commons-net-3.5.jar)
package com.esb.component; import java.io.BufferedOutputStream; import java.
2210 0
|
开发工具 git Shell
使用git代替FTP部署代码到服务器的例子
这篇文章主要介绍了使用git代替FTP部署代码到服务器的例子,这种方法可以节省流量、节省时间,需要的朋友可以参考下 本地开发完成后,通常会在服务器上部署,有人会使用ftp,有人会使用scp, ftp和scp的时候为了方便我们通过还要tar或者zip一下, 这些做法也很不错,但是它有一些问题 1.
1379 0