FTP Server

import socket
import struct
from concurrent.futures import ThreadPoolExecutor
import json
import hashlib
import os
import time
from demo import common_utils PUT_FILE_DIR = r'C:\x\LuffyFTP\sharefile\server\put'
GET_FILE_DIR = r'C:\x\LuffyFTP\sharefile\server\get'
IP_PORT = ('127.0.0.1', 9999) def run_forever():
"""
启动socket
:return:
"""
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(IP_PORT)
server_socket.listen(5)
print('Server Start,IP:%s, LISTENING PORT: %s.' %
IP_PORT)
pool = ThreadPoolExecutor(10)
while True:
conn, client_addr = server_socket.accept()
print('创建一个新的线程,和客户端{}通信'.format(client_addr))
pool.submit(take_over_connection, conn, client_addr) def take_over_connection(conn, client_addr):
"""
用来接管socket链接,每个线程接管一个链接
:param conn:
:param client_address:
:return:
"""
print('MyServer')
server = MyServer(conn, client_addr)
server.handle_cmd() class MyServer(object):
"""
处理客户端所有的交互socket server
"""
STATUS = {
300: 'File not exist !',
301: 'File exist , and the msg include the file size!',
302: 'File not exist !!!'
} def __init__(self, conn, client_addr):
self.conn = conn
self.client_addr = client_addr def handle_cmd(self):
"""
处理用户命令交互
:return:
"""
print('handle_cmd')
while True:
try:
# 收到报头长度
recv_pack = self.conn.recv(4)
if not recv_pack:
print(
'connect {} is lost ……'.format(
self.client_addr))
break
# 解析报头
recv_length = struct.unpack('i', recv_pack)[0] header_data = self.conn.recv(recv_length)
# json_data
json_data = json.loads(header_data.decode('utf-8'))
print('recv data >>> {}'.format(json_data))
action_type = json_data.get('action_type') if action_type:
# 使用反射
if hasattr(self, '_{}'.format(action_type)):
func = getattr(self, '_{}'.format(action_type))
func(json_data)
else:
print('invalid command')
except ConnectionResetError: # 适用于windows操作系统
break def send_response(self, status_code, **kwargs):
"""
向客户端发送响应吗
:param status:
:return:
"""
# 构造消息头
message = {
'status': status_code,
'status_message': self.STATUS.get(status_code)
}
message.update(kwargs) # 更新消息
message_json = json.dumps(message)
# 为防止粘包,封装消息包
header_byte = message_json.encode('utf-8')
# 先发送报头的长度
self.conn.send(struct.pack('i', len(message_json)))
print('发送response报头的长度: {}'.format(len(message_json)))
print('发送response报头内容:{}'.format(message))
# 发送报头
self.conn.send(header_byte) def _get(self, data):
"""
下载文件,如果文件存在,发送状态码+文件大小+md5,发送文件
不存在,发送状态码
:param data:
:return:
"""
print('_get {}'.format(data))
file_path = os.path.join(
GET_FILE_DIR,
data.get('file_name'))
if os.path.isfile(file_path):
file_size = os.path.getsize(file_path)
print(
'file_path: {} file_size: {} '.format(
file_path, file_size))
self.send_response(301, file_size=file_size, md5=common_utils.get_md5(
file_path), server_file_dir=os.path.dirname(file_path))
print('read to send file >>>', data.get('file_name'))
with open(file_path, 'rb') as f:
for line in f:
self.conn.send(line)
else:
print('send file {} done'.format(file_path)) else:
self.send_response(302) def _put(self, data):
"""
拿到文件名和大小,检测本地是否存在相同文件
如果存在,创建新文件local_file_name+timestamp
如果存在,创建新文件local_file_name
:param data:
:return:
"""
print('_put {}'.format(data))
file_size = data.get('file_size')
file_name = data.get('file_name')
file_path = os.path.join(
PUT_FILE_DIR,
file_name) client_md5 = data.get('md5')
if os.path.isfile(file_path):
print('file is exist')
file_path = '{}.{}'.format(file_path, str(int(time.time())))
tmp_file = '{}.down'.format(file_path)
print('tmp_file:', tmp_file) f = open(tmp_file, 'wb')
recv_size = 0
print('put file {} start >>> '.format(file_path))
while recv_size < file_size:
data = self.conn.recv(8192) # 接收文件内容
f.write(data)
recv_size += len(data)
else:
print("\n")
print(
'-- file [{}] put done, received size [{}]'.format(file_name, common_utils.bytes2human(
os.path.getsize(tmp_file))))
f.close() os.rename(tmp_file, file_path)
server_md5 = common_utils.get_md5(file_path) if server_md5 == client_md5:
print('文件上传完整与客户端一致') if __name__ == '__main__':
run_forever()

  

FTP Client

import argparse
import socket
import json
import struct
import sys
import os
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(BASE_DIR)
from demo import common_utils PUT_FILE_PATH = r'C:\x\LuffyFTP\sharefile\client\put'
GET_FILE_PATH = r'C:\x\LuffyFTP\sharefile\client\get'
IP_PORT = ('127.0.0.1', 9999) class FtpClient():
"""
ftp客户端
""" def __init__(self):
self.client_sock = None
self.make_connect() def make_connect(self):
"""
连接服务器
:return:
"""
try:
self.client_sock = socket.socket(
socket.AF_INET, socket.SOCK_STREAM)
print('连接服务器')
self.client_sock.connect(IP_PORT) # 连接服务器 except Exception as e:
print('连接服务器异常', e) def interactive(self):
"""
交互
:return:
"""
menu = """
1. 下载文件 get 1.txt
2. 上传文件 put 1.txt
3. 退出 bye """
print(menu)
while True:
user_input = input('请输入 >>> ').strip()
if not user_input:
continue cmd_list = user_input.split()
if hasattr(self, '_{}'.format(cmd_list[0])):
func = getattr(self, '_{}'.format(cmd_list[0]))
func(cmd_list) # get def send_msg(self, action_type, **kwargs):
"""
打包消息,发送到服务器
:param action_type:
:param kwargs:
:return:
"""
cmd = {
'action_type': action_type, }
cmd.update(kwargs) # 更新字典
cmd_json = json.dumps(cmd)
# 为防止粘包,封装包
header_byte = cmd_json.encode('utf-8')
# 先发送报头的长度
self.client_sock.send(struct.pack('i', len(cmd_json)))
print('发送auth报头的长度: {}'.format(len(cmd_json)))
print('发送auth报头内容:{}'.format(cmd_json))
# 发送报头
self.client_sock.send(header_byte) def arg_check(self, cmd_args, len_args):
if len(cmd_args) != len_args:
print(
'must provide {} parameters but received {}'.format(len_args,
len(cmd_args)))
return False
else:
return True def get_response(self):
"""
收到服务器向客户端发送的响应
:return:
"""
# 收到报头长度
recv_pack = self.client_sock.recv(4)
if recv_pack:
# 解析报头
recv_length = struct.unpack('i', recv_pack)[0]
header_data = self.client_sock.recv(recv_length)
# json_data
json_data = json.loads(header_data.decode('utf-8'))
print('recv response >>> {}'.format(json_data)) return json_data
else:
print('recv_pack is null !!!')
return None def _get(self, cmd_args):
"""
得到文件,发送到远程,等待返回消息,
等待文件,循环收文件
:param cmd_args:
:return:
"""
if self.arg_check(cmd_args, 2): file_name = cmd_args[1] # get filename
self.send_msg('get', file_name=file_name) response_data = self.get_response()
if response_data.get('status') == 301:
file_size = response_data.get('file_size')
server_md5 = response_data.get('md5') file_path = os.path.join(
GET_FILE_PATH, file_name) recv_size = 0
p = self.progress_bar(file_size) # 进度条
p.send(None)
print('get file {} start >>> '.format(file_name))
tmp_file = '{}.down'.format(file_path)
with open(tmp_file, 'wb') as f: # 写下载文件
# 序列化保存数据 while recv_size < file_size:
data = self.client_sock.recv(8192)
f.write(data)
recv_size += len(data)
p.send(recv_size)
else:
print("\n")
print(
'-- file [{}] recv done, received size [{}]'.format(file_name, file_size)) if os.path.isfile(file_path): # 如果文件存在,删除后覆盖文件
os.remove(file_path)
os.rename(tmp_file, file_path) client_md5 = common_utils.get_md5(file_path)
if server_md5 == client_md5:
print('文件下载完整与服务端一致')
else:
print(response_data.get('status_message')) def _put(self, cmd_args):
"""
1.上传本地文件到服务器
2.确保本地文件存在
3.把文件名和文件大小发送到远程
4.发送文件内容
:return:
"""
if self.arg_check(cmd_args, 2):
local_file_name = cmd_args[1] # put filename
full_path = os.path.join(PUT_FILE_PATH, local_file_name)
if os.path.isfile(full_path):
total_size = os.path.getsize(full_path)
self.send_msg(
'put',
file_name=local_file_name,
file_size=total_size, md5=common_utils.get_md5(full_path)) p = self.progress_bar(total_size)
p.send(None)
upload_size = 0
with open(full_path, 'rb') as f: # 发送文件
for line in f:
self.client_sock.send(line)
upload_size += len(line)
p.send(upload_size)
else:
print("\n")
print('file upload done'.center(50, '-')) else:
print(
'file [{}] is not exist !!!'.format(local_file_name)) def _bye(self, cmd_args):
"""
退出
:return:
"""
print("bye")
self.client_sock.close()
exit(0) @staticmethod
def progress_bar(total_size):
"""
显示进度条
:param total_size:
:return:
"""
current_percent = 0
last_percent = 0
while True:
recv_size = yield current_percent
current_percent = int(recv_size / total_size * 100) print("#" * int(current_percent / 4) + '{percent}%'.format(percent=int(current_percent)), end="\r",
flush=True) if __name__ == '__main__':
c = FtpClient()
c.interactive()

 

common_util

import logging
from logging import handlers
import os
from tkinter import Tk, filedialog
import os
import hashlib def bytes2human(n):
# 文件大小字节单位转换
symbols = ('K', 'M', 'G', 'T', 'P', 'E')
prefix = {}
for i, s in enumerate(symbols):
# << 左移” 左移一位表示乘2 即1 << 1=2,二位就表示4 即1 << 2=4,
# 10位就表示1024 即1 << 10=1024 就是2的n次方
prefix[s] = 1 << (i + 1) * 10
for s in reversed(symbols):
if n >= prefix[s]:
value = float(n) / prefix[s]
return '%.2f%s' % (value, s)
return "%sB" % n def get_md5(file_path):
"""
得到文件MD5
:param file_path:
:return:
"""
if os.path.isfile(file_path):
file_size = os.stat(file_path).st_size
md5_obj = hashlib.md5() # hashlib
f = open(file_path, 'rb') # 打开文件
read_size = 0
while read_size < file_size:
read_byte = f.read(8192)
md5_obj.update(read_byte) # update md5
read_size += len(read_byte)
hash_code = md5_obj.hexdigest() # get md5 hexdigest
f.close()
print('file: [{}] \nsize: [{}] \nmd5: [{}]'.format(
file_path, bytes2human(read_size), hash_code))
return str(hash_code) def get_dir_size_count(dir):
"""
获得文件夹中所有文件大小和文件个数
:param dir:
:return:
"""
size = 0
count = 0
for root, dirs, files in os.walk(dir):
size_li = [os.path.getsize(os.path.join(root, name))
for name in files]
size += sum(size_li)
count += len(size_li)
print('目录{} 文件个数{}, 总共大小约{}'.format(dir, count, bytes2human(size)))
return count, size def brows_local_filename(title='choose a file', force=False):
"""
Select a local file by filedialog of tkinter. Return an exist file path.
:param title:
:param force: If force is True user must choose a file.
:return:
"""
tk = Tk()
tk.withdraw()
tk.wm_attributes('-topmost', 1)
while True:
filename = filedialog.askopenfilename(title=title)
if not force or filename:
break
tk.destroy()
return filename def brows_save_filename(title='save as'):
"""
Select a local path to save a file by filedialog of tkinter.Return a path for saving file.
:param title:
:return:
"""
tk = Tk()
tk.withdraw()
tk.wm_attributes('-topmost', 1)
filename = filedialog.asksaveasfilename(title=title)
tk.destroy()
return filename

Python FTP文件传输的更多相关文章

  1. Python实现终端FTP文件传输

    实现终端FTP文件传输 代码结构: .├── client.py├── readme.txt└── server.py 运行截图: readme.txt tftp文件服务器 项目功能: * 客户端有简 ...

  2. FTP 文件传输服务

    昨晚心血来潮,尝试用python写了一个ftp文件传输服务,可以接收指令,从远程ftp服务器同步指定目录数据,最后没用上,开源出来. https://github.com/jadepeng/ftp_t ...

  3. 【RL-TCPnet网络教程】第35章 FTP文件传输协议基础知识

    第35章      FTP文件传输协议基础知识 本章节为大家讲解FTP(File Transfer Protocol,文件传输协议)的基础知识,方便后面章节的实战操作. (本章的知识点主要整理自网络) ...

  4. FTP文件传输服务

    FTP文件传输服务 一 .FTP 连接及传输的模式 l  控制连接:TCP21,用于发送FTP命令信息. l  数据连接:TCP 20, 用于上传下载数据. · 数据连接建立的类型: ·主动模式: 服 ...

  5. SSIS 学习之旅 FTP文件传输-脚本任务

    这一章主要讲解一下用脚本怎么把CSV文件抛送到FTP服务器上 设计:   通过Demon库的Users表数据生成CSV文件.   生成后的CSV文件抛送到FTP指定目录下. 控件的使用这里就不做详细讲 ...

  6. FTP文件传输

    FTP项目作业要求:1.用户加密认证2.允许同时多用户登录3.每个用户有自己的家目录,且只能访问自己的家目录4.对用户进行磁盘配额,每个用户的可用空间不同5.允许用户在ftp server上随意切换目 ...

  7. FTP(文件传输协议)工作原理

    目前在网络上,如果你想把文件和其他人共享.最方便的办法莫过于将文件放FTP服务器上,然后其他人通过FTP客户端程序来下载所需要的文件. 1.FTP架构 如同其他的很多通讯协议,FTP通讯协议也采用客户 ...

  8. FTP文件传输协议两种模式 ftp协议集,错误码集,ftp客户端命令集

    TCP/IP协议中,FTP标准命令TCP端口号为21,Port方式数据端口为20.FTP协议的任务是从一台计算机将文件传送到另一台计算机,它与这两台计算机所处的位置.联接的方式.甚至是是否使用相同的操 ...

  9. Linux菜鸟成长日记 ( Linux 下的 ftp 文件传输协议 )

    https://blog.csdn.net/buster_zr/article/details/80244542 FTP FTP 是 File Transfer Protocol (文件传输协议)的英 ...

随机推荐

  1. shell for if

    #!/bin/bash ..} do ];then j="${i}" else j="${i}" fi echo $j >> venn.log ec ...

  2. google pay app权限使用说明

    android.permission.CAMERA. 个人中心使用头像时需要使用该权限. android.permission.READ_PHONE.获取用户DeviceId,作为用户单点登录唯一值.

  3. auto和decltype(c++11)

    1.auto 1)auto是一个类型说明符(类型说明符就是像int.double这样的),用来定义一个变量,它可以让编译器去分析表达式的类型,并使用该表达式的值去初始化变量 //auto定义的变量必须 ...

  4. Spring Boot项目简单上手+swagger配置+项目发布(可能是史上最详细的)

    Spring Boot项目简单上手+swagger配置 1.项目实践 项目结构图 项目整体分为四部分:1.source code 2.sql-mapper 3.application.properti ...

  5. c#内存中创建反射

    代码 IWFP_SYS_Bytes mywfpbyte; /**/ byte[] bin=null; using (FileStream fs = new FileStream(filename, F ...

  6. java se的那些细节

    局部变量:方法体内或语句块内,不能有修饰符 成员变量:与类的对象共存,可以有修饰符 类属性:与类共存,可以有修饰符 一.局部变量:必须先赋值,才能使用,如果不赋初值,则会报错,即没有默认的始使值,而基 ...

  7. Gibs抽样

    /* * Copyright (C) 2007 by * * Xuan-Hieu Phan * hieuxuan@ecei.tohoku.ac.jp or pxhieu@gmail.com * Gra ...

  8. Zookeeper C++编程实战之配置更新

    CZookeeperHelper:https://github.com/eyjian/libmooon/blob/master/include/mooon/net/zookeeper_helper.h ...

  9. checked 选择框选中

    移除属性,两种方式都可 $browsers.removeAttr("checked"); $browsers.attr("checked",false); // ...

  10. Java理论学时第四节。课后作业。

    请查看String.equals()方法的实现代码,注意学习其实现方法. public class StringEquals { public static void main(String[] ar ...