有時須要在局域網傳輸文件,老是要用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服務?問題已解決,把多線程換成多進程就能夠了,多進程自帶結束進程的方法