1 ftplib报错 ftplib 'latin-1' codec can't encode characters 解决方法: 找到引用的ftplib.py 将encoding ='latin-1'改为encoding ='utf-8' 2 打包exe pycharm→file→settings→project→interpreter 增加pyinstall view→tool→terminal pyinstaller -F -w main.py 3 Failed to execute 配置项在配置文件中 由于是2exe后执行文件路径变更,需要将app.conf拷贝到exe所在文件夹 4 python源码 ``` # python是一门解释性语言,由于需要解释器,所以在运行的时候需要安装运行环境 # 目前IDE选择的是jetbrains的pycharm,主要是因为较简单地实现了venv的虚拟运行环境,不会与系统本身的python环境冲突 # 如果要在windows中使用,建议打包成为exe的可执行文件 # pycharm→file→settings→project→interpreter,增加pyinstall,然后view→tool→terminal,执行pyinstaller -F -w main.py # 如果打包后运行exe出现Failed to execute,由于是2exe后执行文件路径变更,需要将app.conf拷贝到exe所在文件夹 # 如果调试时报错ftplib 'latin-1' codec can't encode characters,找到引用的ftplib.py,将encoding ='latin-1'改为encoding ='utf-8' import os # 导入系统包,主要用于路径的获取,文件的读写 import configparser # 把路径和ftp都放到了配置文件,以后好更改 import ftplib # 导入ftp相关的包 conf = configparser.ConfigParser() # 实例化配置文件 def read_conf(option): """ 定义了一个读取文件的方法,需要传参option :param option:config文件中有section分类和option值,这里配置项较为简单,全部放入一个section中,只传参option值即可 :return:返回值,调用这个方法后,会返回配置文件中的配置项 Python不是静态类型语言,也不属于标准的强类型语言,所有返回值的类型需要注意 """ base_dir = os.path.abspath(os.path.dirname(__file__)) # 获取项目的绝对路径 conf.read(base_dir + '/app.conf') # config文件路径 cinfig_option = conf.get("config", option) # 获取指定section 的option值 return cinfig_option loacl_path = read_conf("loacl_path") # 获取FTP服务器地址 ftp_ip = read_conf("ftp_ip") # 获取FTP用户名 ftp_user = read_conf("ftp_user") # 获取FTP密码 ftp_pass = read_conf("ftp_pass") # 获取本地文件夹目录 ftp_path = read_conf("ftp_path") # 获取ftp备份目录 session = ftplib.FTP(host=ftp_ip, user=ftp_user, passwd=ftp_pass) # 实例化ftp连接 def upload_dir(path_source, session, target_dir=None): files = os.listdir(path_source) # 返回指定的文件夹包含的文件或文件夹的名字的列表 last_dir = os.path.abspath(loacl_path) # 先记住之前在哪个工作目录中 os.chdir(path_source) # 然后切换到目标工作目录 if target_dir: current_dir = session.pwd() # 获取ftp的当前路径 try: session.mkd(target_dir) # 穿件备份文件夹 except Exception as e: pass # 属于偷懒,先try,报错才到except,一般会写成except Exception as e,然后print(e),逐步缩小异常类型排查错误 finally: session.cwd(os.path.join(current_dir, target_dir)) # 拼接当前路径和备份文件夹,并切换到该路径 for file_name in files: # for循环遍历files列表中的文件 current_dir = session.pwd() # 获取ftp的当前路径 if os.path.isfile(path_source + r'/{}'.format(file_name)): # 调用os判断是文件还是目录, upload_file(path_source, file_name, session, current_dir) # 如果是文件就执行后面定义的upload_file方法 elif os.path.isdir(path_source + r'/{}'.format(file_name)): # 如果是目录 current_dir = session.pwd() # 获取当前ftp目录 try: session.mkd(file_name) # 尝试新建目录 except: pass session.cwd("%s/%s" % (current_dir, file_name)) # 切换到新建的目录 # 递归调用了自己这个方法本身,然后target_dir未传参,所以会一直到文件夹中不含子文件夹才停止 upload_dir(path_source + r'/{}'.format(file_name), session) session.cwd(current_dir) # 之前路径可能已经变更,需要再回复到之前的路径里 os.chdir(last_dir) # 切换到进入方法之前记录的最后的路径,以免递归调用时出错 def upload_file(path, file_name, session, target_dir, callback=None): cur_dir = session.pwd() # 记录当前 ftp 路径 if target_dir: try: # 存在指定路径 就先创建 session.mkd(target_dir) except: pass finally: session.cwd(os.path.join(cur_dir, target_dir)) # 切换到指定路径 print("path:%s \r\n\t file_name:%s" % (path, file_name)) # 打印路径和文件信息 file = open(os.path.join(path, file_name), 'rb') # 要上传的文件 session.storbinary('STOR %s' % file_name, file, callback=callback) # 文件上传 file.close() # 关闭连接 session.cwd(cur_dir) # 切换回初始目录 upload_dir(loacl_path, session, ftp_path) # 执行之前定义的文件夹上传的方法 ```