有时须要在局域网传输文件,老是要用U盘传输,要是多我的都须要一样的文件,U盘就有点麻烦了,Windows上的文件共享配置步骤不多,可是常常由于各类缘由失败,又要检查来检查去的。因而考虑到经过FTP协议来传输文件,可是出名的FTP服务器软件相似Serv-U 这种,功能是很强大,可是配置也不少,我只是临时传输下文件,但愿能够免安装,一键启动FTP,一键关闭FTP。因而就想使用python实现FTP服务器,再打包成exe文件。python
win 7 64位,Python 3.6.2,pyftpdlib,wxpython,pyinstaller 3.3.1服务器
经过pyftpdlib库实现FTP 功能ftpserver.py,使用wxpython实现GUI界面wxgui.py,在wxgui.py上生成配置文件config.ini,ftpserver.py再获取config.ini中的信息启动FTP,最后使用pyinstaller打包成exe可执行文件。多线程
不过刚开发好的软件,应该有挺多BUG的,之后使用过程当中再慢慢改进。
打开软件:
选择FTP目录:
启动FTP:app
此次小项目中使用到了ini格式的配置文件,该配置文件提供给FTP服务启动须要的配置信息(是否匿名,FTP端口等),还能够记录FTP启动时GUI界面状态,使界面关闭后打开仍是FTP启动时的界面。
ini 文件是文本文件,中间的数据格式通常为: socket
[Section1 Name] KeyName1=value1 KeyName2=value2 ...
python能够经过configparse内置模块写入读取配置信息
该界面启动就执行下面的Python语句:ide
import configparse config=configparser.ConfigParser() config.add_section('ftpd')#添加一个新的section config.set('ftpd','anonymous',str(not check.GetValue()))#判断复选框是否选中 config.set('ftpd','user',usertext.GetValue())#获取用户名 config.set('ftpd','password',passtext.GetValue())#获取密码 config.set('ftpd','port',porttext.GetValue())#获取端口 config.set('ftpd','dir',dirtext.GetValue())#获取目录 #写入到config.ini配置 with open('config.ini','w') as conf: config.write(conf)
写入的配置文件config.ini以下:函数
[ftpd] anonymous = False user = aaa password = aaa port = 21 dir = E:\
接着就是ftpserver.py如何获取config.ini配置文件的信息了oop
config = configparser.ConfigParser() config.read('config.ini') config.get('ftpd',user)#获取用户名 config.get('ftpd',password)#获取密码 ...
wxgui.py也能够经过config.ini配置文件来恢复程序界面关闭前的模样ui
def getconfig(v):#pyserver.py中的函数 #获取config.ini配置文件的信息 config = configparser.ConfigParser() config.read('config.ini') return config.get('ftpd',v)
if os.path.isfile('config.ini'):#config.ini文件存在的话,就获取配置文件信息。 if ftpserver.getconfig('anonymous')=='False': check.SetValue(1)#设置复选框为选中状态 usertext.SetEditable(1)#容许编辑 passtext.SetEditable(1) usertext.SetValue(ftpserver.getconfig('user'))#设置用户名输入框的值 passtext.SetValue(ftpserver.getconfig('password')) porttext.SetValue(ftpserver.getconfig('port')) dirtext.SetValue(ftpserver.getconfig('dir'))
实现FTP功能代码ftpserver.py:url
# coding:utf-8 from pyftpdlib.authorizers import DummyAuthorizer from pyftpdlib.handlers import FTPHandler from pyftpdlib.servers import FTPServer import logging import configparser import os def ftpd(): authorizer = DummyAuthorizer() if getconfig('anonymous')=='True': #添加匿名用户 authorizer.add_anonymous(getconfig('dir'),perm='elradfmwM') else: #添加用户,须要帐号和密码登陆 authorizer.add_user(getconfig('user'), getconfig('password'), getconfig('dir'), perm='elradfmwM') handler = FTPHandler#初始化处理客户端命令的类 handler.authorizer = authorizer#选择登陆方式(是否匿名) logging.basicConfig(filename='ftpd.log', level=logging.INFO) #日志信息写入ftpd.log address = ('0.0.0.0', getconfig('port'))#设置服务器的监听地址和端口 server = FTPServer(address, handler) server.max_cons = 256 #给连接设置限制 server.max_cons_per_ip = 5 server.serve_forever() # 启动FTP def getconfig(v): #获取config.ini配置文件的信息 config = configparser.ConfigParser() config.read('config.ini') return config.get('ftpd',v) if __name__ == '__main__': ftpd()
实现GUI界面代码wxgui.py:
# -*- coding:utf-8 -*- import wx import os import sys import configparser import ftpserver import time import threading import socket def onclick(event): #选择FTP目录 dlg=wx.DirDialog(window,'选择共享目录') if dlg.ShowModal() == wx.ID_OK: dirtext.SetValue(dlg.GetPath()) def onclose(event): #退出事件 if startbutton.GetLabel()=='关闭FTP': dlg1 = wx.MessageDialog(None, "FTP正在运行,确认退出吗?", "退出", wx.YES_NO | wx.ICON_EXCLAMATION) if dlg1.ShowModal()==wx.ID_YES: sys.exit() else: sys.exit() def startftp(event): #点击 启动FTP 按钮事件 global t if startbutton.GetLabel()=='启动FTP': startbutton.SetLabel('关闭FTP') #把FTP启动信息写入config.ini配置文件中 config=configparser.ConfigParser() config.add_section('ftpd') config.set('ftpd','anonymous',str(not check.GetValue())) config.set('ftpd','user',usertext.GetValue()) config.set('ftpd','password',passtext.GetValue()) config.set('ftpd','port',porttext.GetValue()) config.set('ftpd','dir',dirtext.GetValue()) with open('config.ini','w') as conf: config.write(conf) time.sleep(1) #建立线程启动FTP t=threading.Thread(target=ftpserver.ftpd) t.setDaemon(True) t.start() iplist=socket.gethostbyname_ex(socket.gethostname())[2] ftpurl='' if iplist : for ip in iplist: ftpurl+='FTP地址:ftp://'+ip+':'+porttext.GetValue()+'\n' urllabel.SetLabel(ftpurl) else: dlg1 = wx.MessageDialog(None, "FTP正在运行,确认退出吗?", "退出", wx.YES_NO | wx.ICON_EXCLAMATION) if dlg1.ShowModal()==wx.ID_YES: sys.exit() def onchecked(event): #复选框事件 usertext.SetEditable(event.IsChecked()) passtext.SetEditable(event.IsChecked()) app = wx.App()#实例化wx.App #建立顶级窗口,而且窗口不可改变尺寸 window = wx.Frame(None,title = "FTP服务器", size = (390, 260), pos=(400,300),style=wx.DEFAULT_FRAME_STYLE ^ wx.RESIZE_BORDER ^ wx.MAXIMIZE_BOX) #绑定退出事件 window.Bind(wx.EVT_CLOSE,onclose) #建立panel panel = wx.Panel(window) font = wx.Font(11, wx.SWISS, wx.NORMAL, wx.NORMAL) #建立复选框,并绑定事件 check=wx.CheckBox(panel,-1,'使用帐户密码登陆',pos=(20,20),size=(200,-1)) check.SetFont(font) window.Bind(wx.EVT_CHECKBOX,onchecked) #建立静态文本和文本输入框 userlabel = wx.StaticText(panel, label = "用户名:", pos=(35,45)) userlabel.SetFont(font) usertext = wx.TextCtrl(panel,-1,'',size=(95,20),pos=(90,42),style = wx.TE_READONLY) passlabel = wx.StaticText(panel, label = "密码:",pos=(195,45)) passlabel.SetFont(font) passtext = wx.TextCtrl(panel,-1,'',size=(95,20),pos=(235,42),style = wx.TE_PASSWORD) passtext.SetEditable(False) dirlabel = wx.StaticText(panel, label = "FTP目录:",pos=(23,72)) dirlabel.SetFont(font) dirtext = wx.TextCtrl(panel,-1,os.getcwd(),size=(215,20),pos=(88,70),style = wx.TE_READONLY) #建立按钮,而且绑定按钮事件 button=wx.Button(panel,-1,'更改',pos=(310,70),size=(40,20)) button.SetFont(wx.Font(9, wx.SWISS, wx.NORMAL, wx.NORMAL)) window.Bind(wx.EVT_BUTTON,onclick,button) portlabel = wx.StaticText(panel, label = "FTP端口:",pos=(23,104)) portlabel.SetFont(font) porttext = wx.TextCtrl(panel,-1,'21',size=(51,20),pos=(88,102)) startbutton=wx.Button(panel,-1,'启动FTP',pos=(160,130),size=(70,30)) startbutton.SetFont(wx.Font(12, wx.SWISS, wx.NORMAL, wx.NORMAL)) window.Bind(wx.EVT_BUTTON,startftp,startbutton) #加载配置文件,使程序恢复到退出前的界面 if os.path.isfile('config.ini'): if ftpserver.getconfig('anonymous')=='False': check.SetValue(1) usertext.SetEditable(1) passtext.SetEditable(1) usertext.SetValue(ftpserver.getconfig('user')) passtext.SetValue(ftpserver.getconfig('password')) porttext.SetValue(ftpserver.getconfig('port')) dirtext.SetValue(ftpserver.getconfig('dir')) urllabel = wx.StaticText(panel, label = "", pos=(80, 170)) urllabel.SetFont(font) window.Show(True)#窗口可见 app.MainLoop() #主循环,处理事件
若是顺利的话,两条命令就能够成功打包了,反正我是没那么顺利的了
pip安装pyinstaller
pip install pyinstaller
开始打包:
pyinstaller -F wxgui.py -w
-F 参数:是生成单个可执行文件
-w参数:由于个人是GUI程序,因此我使用这参数去掉命令行窗口
生成的文件:
此次开发这个小项目中遇到了个问题,百度谷歌都找不到解决的办法。程序建立个线程去启动FTP服务,当我想要中止FTP服务时,在不退出程序的状况下该如何去中止FTP服务?问题已解决,把多线程换成多进程就能够了,多进程自带结束进程的方法