开发环境
服务器:centos6.5
自带python2.6.6以及socket库
客户端:windows10
python2.7.9以及socket库,Tkinter库,MySQLdb库
运行方式
服务器:后台运行server.py脚本
客户端:运行打包的exe文件,无需要求客户端配置python环境
效果展示
功能介绍
- 通过查询框可以模糊匹配相关文件名并展示。
- 通过选择对应文件名,文件名会同步到下载输入框,点击下载即可从远程主机下载到本机D:/download(可自行更换),下载前将清空D:/download原有内容(可自行考量是否需要注释该部分功能,我这是项目需求)。
- 可通过程序上传按钮上传本机文件至远程主机,上传前加了密码验证弹出框。
- 服务器端,有shell脚本去驱动python脚本后台运行。python脚本会实时保存上传下载的记录文件。
代码展示
一、client.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Date : 2020-12-08
# @Author : GuoZhiQiang
# @Desc : 增加模糊查询文件名功能
# @Version : 2.0
import socket
import shutil
import time
import Tkinter as tk
from Tkinter import *
import MySQLdb
import ttk
import tkMessageBox
import tkFileDialog
import tkSimpleDialog
import os
import sys
# socket连接
ip = '127.0.0.1'
port = 8888
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
'''
文件接收
'''
def recvfile(filename):
f = open(filename, 'wb')
while True:
data = s.recv(4096)
if data == 'EOF':
tkMessageBox.showinfo("提示","下载成功!!!")
break
else:
f.write(data)
f.close()
'''
文件发送
'''
def sendfile(filename):
f = open(filename, 'rb')
while True:
data = f.read(4096)
if not data:
break
s.sendall(data)
f.close()
time.sleep(1)
s.sendall('EOF')
tkMessageBox.showinfo("提示","上传成功!!!")
'''
通信确认函数
'''
def confirm(s, client_command):
s.send(client_command)
data = s.recv(4096)
if data == 'ready':
return True
else:
return False # 此处存在无文件,或通信阻塞导致的问题,应加以区分
'''
上传下载总控制函数
'''
def control(type,name):
try:
if type == "put":
password = tkSimpleDialog.askstring("密码输入","请输入上传口令",show="*")
if password:
if password == "123456":
full_path = tkFileDialog.askopenfilename(initialdir='D:\\upload')
filename = os.path.basename(full_path)
if confirm(s, 'put '+filename):
upload_path = full_path # 上传文件路径
sendfile(upload_path)
else:
tkMessageBox.showerror("错误","服务器上传文件错误!!!")
else:
tkMessageBox.showerror("错误","上传口令错误!!!")
elif type == "get":
ask = tkMessageBox.askokcancel('提示', '下载将清空原程序文件哦,确定要执行此操作吗?')
if ask:
if name != "":
if confirm(s, 'get '+name):
download_path = 'D:\\download' + '\\' + name # 下载文件到本地路径
shutil.rmtree('D:\\download') # 清空原文件夹
time.sleep(0.5) # 适当延时,防止删除时间过长
os.mkdir('D:\\download') # 新建原文件夹
recvfile(download_path)
else:
tkMessageBox.showerror("提示", "服务器没有你要下载的程序!")
else:
tkMessageBox.showwarning("提示","请填写文件名!")
except socket.error as e:
tkMessageBox.showerror("异常",e)
'''
数据搜索与绑定
'''
def data_search(window,tree,search_model):
# mysql数据库连接 请对应你自己的Mysql数据
conn = MySQLdb.connect(host='127.0.0.1', user='root', passwd='123456', charset='utf8')
conn.select_db('File') # 你的数据库名
cursor = conn.cursor()
model = search_model+"%"
try:
cur = cursor.execute('select * from file_name where filename like \'%s\''%model)
if cur > 0 :
cursor.scroll(0,mode='absolute')
results = cursor.fetchall()
if len(results) <= 0:
tkMessageBox.showwarning("提示","查询无返回数据!")
# 3.向tree写入数据,先清空TreeView控件
tree.delete(*tree.get_children())
for i in range(len(results)):
tree.insert('', i, text=i, values=(i+1, results[i][1]))
else:
tkMessageBox.showwarning("提示", "查询无返回数据!")
except:
import traceback
traceback.print_exc()
conn.rollback()
finally:
cursor.close()
conn.close()
if __name__ == '__main__':
window = tk.Tk()
# 设置主窗体大小
winWidth = 600
winHeight = 400
# 获取屏幕分辨率
screenWidth = window.winfo_screenwidth()
screenHeight = window.winfo_screenheight()
# 计算主窗口在屏幕上的坐标
x = int((screenWidth - winWidth) / 2)
y = int((screenHeight - winHeight) / 2)
window.title("ICT程序上传下载工具_V1.1")
window.geometry('%sx%s+%s+%s'%(winWidth,winHeight,x,y))
window.minsize(600,400)
try:
# 设置socket的连接超时为1秒以达到快速弹出失败提醒
s.settimeout(1)
s.connect((ip, port))
if os.path.exists('D:\\download') == False:
os.mkdir('D:\\download')
time.sleep(1)
except socket.error as e:
tkMessageBox.showerror("错误","无法连接到服务端!请检查网络连接是否正常!或重新运行服务端脚本run_Scoket_Server.sh")
sys.exit(0)
frame = tk.Frame(window)
frame.place(rely=0.5, relx=0.5, x=-230, y=-170, width=600, height=400)
download_label = tk.Label(frame,text="下载文件名:")
download_label.grid(row=0,column=0,padx=5,pady=5)
download_entry = tk.Entry(frame,bd=5,width=40)
download_entry.grid(row=0,column=1)
download_btn = tk.Button(frame,text="程序下载",bg='white',width=10,height=1,command=lambda :control('get',download_entry.get()),activebackground='#00ffff')
download_btn.grid(row=0,column=2,padx=5,pady=5)
# 数据查询显示
# 定义列的名称
columns = ("id", "filename")
tree = ttk.Treeview(frame, show="headings", columns=columns,selectmode=tk.BROWSE)
# 设置表格文字居中
tree.column("id", anchor="center",width=50)
tree.column("filename", anchor="center",width=400)
# 设置表格头部标题
tree.heading("id", text="序号")
tree.heading("filename", text="文件")
tree.grid(row=2,column=0,columnspan=3,pady=2)
# 获取当前点击行的值
def treeviewClick(event): # 单击
for item in tree.selection():
item_text = tree.item(item, "values")
print(item_text)
# 鼠标左键抬起
# tree.bind('<ButtonRelease-1>', treeviewClick)
# 鼠标选中一行回调
def selectTree(event):
for item in tree.selection():
item_text = tree.item(item, "values")
download_entry.delete(0, END)
download_entry.insert(0, item_text[1])
# 选中行
tree.bind('<<TreeviewSelect>>', selectTree)
# 设置查询框
search_label=tk.Label(frame, text="查询型号:")
search_label.grid(row=1,column=0,pady=2)
search_model = tk.Entry(frame, bd=5,width=40)
search_model.grid(row=1,column=1,pady=2)
# 4.点击按钮调用查询函数
select_button = tk.Button(frame, bg='white', text='查询', width=10, height=1,command=lambda: data_search(window, tree, search_model.get()),activebackground='#00ffff')
select_button.grid(row=1,column=2,pady=2)
upload_btn = tk.Button(frame, text="程序上传", command=lambda: control('put', ''),bg='#00ccff',width=20)
upload_btn.grid(row=3,column=1,pady=2)
window.mainloop()
二、server.py
# !/usr/bin/env python
# -*- coding: utf-8 -*-
# @Date : 2020-11-16
# @Author : GuoZhiQiang
# @Version : 1.0
import SocketServer
import time
import datetime
import os
# 定义当前目录
current_dir = os.getcwd()
# 定义一个类
class EddyFtpserver(SocketServer.BaseRequestHandler):
# 定义接收文件方法
def recvfile(self, filename):
print "starting reve file!"
f = open(filename, 'wb')
self.request.send('ready')
time.sleep(0.1)
while True:
data = self.request.recv(4096)
if data == 'EOF':
# 保存上传记录
upload_f = open(current_dir + '/record/upload_record.txt', 'a+')
now_time = datetime.datetime.now()
now_str = datetime.datetime.strftime(now_time, '%Y-%m-%d %H:%M:%S')
print self.client_address
upload_f.write("上传时间:" + now_str + " 上传文件:" + filename + "\n")
upload_f.close()
print "recv file success!"
break
f.write(data)
f.close()
# 定义放送文件方法
def sendfile(self, filename):
print "starting send file!"
flag = 0
if os.path.exists(filename):
self.request.send('ready')
else:
self.request.send('NOFILE')
flag = 1
print "no such file"
if flag == 0:
f = open(filename, 'rb')
while True:
data = f.read(4096)
if not data:
break
self.request.sendall(data)
f.close()
time.sleep(0.5)
self.request.send('EOF')
download_f = open(current_dir + '/record/download_record.txt', 'a+')
now_time = datetime.datetime.now()
now_str = datetime.datetime.strftime(now_time, '%Y-%m-%d %H:%M:%S')
download_f.write("下载时间:" + now_str + " 下载文件:" + filename + "\n")
download_f.close()
print "send file success!"
# SocketServer的一个方法
def handle(self):
print "get connection from :", self.client_address
p=os.system('python write_filename.py') # 运行write_filename.py更新文件名到数据库
print p
while True:
try:
data = self.request.recv(4096)
print "get data:", data
if not data:
print "break the connection!"
break
else:
action, filename = data.split()
# 判断上传
if action == "put":
# 上传文件保存到当前目录下
file_path = current_dir + '/data/' + os.path.split(filename)[1]
print(file_path)
self.recvfile(file_path)
# 判断下载
elif action == 'get':
self.sendfile('./data/'+filename)
else:
print("get error!")
continue
except Exception, e:
print "get error at:", e
finally:
result=os.system('python write_filename.py &') # 运行write_filename.py更新文件名到数据库
print result
if __name__ == "__main__":
host = "127.0.0.1"
port = 8888
# 实例化
s = SocketServer.ThreadingTCPServer((host, port), EddyFtpserver)
s.serve_forever()
建表语句
(在服务主机建表)
create table `file_name` (
`id` int (4),
`filename` varchar (450)
);
write_filename.py
#!/usr/bin/python
import os
import subprocess
'''
获取指定文件夹内所有文件名,并通过write_filename_database.lua写入Mysql数据库file_name表
'''
def read_file_name(file_dir):
for root,dirs,files in os.walk(file_dir):
for file in files:
var = 'save_filename(\'%s\')'%file
result = subprocess.call(['lua','-l','write_filename_database','-e',var],shell=False)
if __name__ == '__main__':
try:
read_file_name('/home/data')
except Exception as ex:
print ex
write_filename_database.lua
(这里和上面的write_filename.py是由于服务端环境无法联网配置导致将遍历文件名,和筛选未存入数据库的文件名存入数据表两个功能用两种语言实现,大家可以用python直接综合写这个功能)
require "lfs"
luasql= require 'luasql.mysql'
function save_filename(file)
--创建环境对象
env = luasql.mysql()
--连接数据库
conn = env:connect("qc_check","root","te998","192.168.253.206",3306)
--设置数据库的编码格式
conn:execute"SET NAMES UTF8"
--执行数据库操作
cur = conn:execute("select count(1) as count from file_name where filename =\'"..file.."\'")
row = cur:fetch(row,"a")
if tonumber(row) == 0 then
cur_1 = conn:execute("insert into file_name(filename) value(\'"..file.."')")
end
conn:close() --关闭数据库连接
env:close() --关闭数据库环境
end
function dirpath(path)
for file in lfs.dir(path) do -- lfs.dir 根据路径获取该路径下的文件名
if file ~= '.' and file ~= '..' then
save_filename(file)
end
end
end
--dirpath("./upload")
--dirpath('./upload')
client代码打包
- 安装pywin32
pip install pywin32
- 安装pyinstaller
pip install pyinstaller
- 打包命令
(可在终端进入到client.py的目录下,再执行以下命令)
pyinstaller -Fw client.py
服务端运行shell脚本(run_server.sh)
#! /bin/bash
#chkconfig:2345 80 90
#description:开机自启动的ICT上传下载服务器端开启脚本
pkill -f Scoket_Server.py
cd /home
nohup python -u Scoket_Server.py>my.log 2>&1 &
echo 'ICT程序上传下载服务端已开启......'