简单的实现文件上传下载功能

简单的实现文件上传下载功能,其中用到了socketserver,可以实现多用户上传和下载文件

服务端代码:

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: Xiaobai Lei
import socket
import subprocess
import os
import json
import hashlib
import struct
import socketserver

class MyServer(socketserver.BaseRequestHandler):
    """文件上传下载服务端"""
    address_family = socket.AF_INET
    socket_type = socket.SOCK_STREAM    # tcp流类型
    allow_reuse_address = False
    max_packet_size = 8192
    coding = 'utf-8'
    request_queue_size = 5  # 队列最长数量
    server_upload_dir = 'file_upload'
    server_download_dir = 'file_download'

    def server_close(self):
        self.request.close()

    def close_request(self, request):
        request.close()

    def handle(self):
        print('from client ', self.client_address)
        while True:
            try:
                head_struct = self.request.recv(4)
                if not head_struct:break

                head_len = struct.unpack('i', head_struct)[0]
                head_json = self.request.recv(head_len).decode(self.coding)
                head_dic = json.loads(head_json)

                action = head_dic['action']
                if hasattr(self, action):
                    func = getattr(self, action)
                    func(head_dic)
            except Exception:
                break

    def put(self, args):
        """实现文件上传"""
        file_path = os.path.normpath(os.path.join(self.server_upload_dir, args['filename']))
        filesize = args['filesize']
        recv_size = 0  # 记录读取文件的大小
        file_md5 = hashlib.md5()    # 计算文件的md5值,保证上传的完整性
        with open(file_path, "wb") as f:
            while recv_size < filesize:
                recv_data = self.request.recv(self.max_packet_size)
                file_md5.update(recv_data)
                f.write(recv_data)
                recv_size += len(recv_data)
        file_md5_code = file_md5.hexdigest()  # 获取文件md5的值,字符串形式
        self.request.send(b"ok")
        recv_md5_code = self.request.recv(self.max_packet_size).decode(self.coding)    # 接收客户端发来的md5值
        if recv_md5_code == file_md5_code:
            print("%s文件上传成功,大小为%s字节!"%(args['filename'], filesize))
            self.request.send(b"1000")
        else:
            self.request.send(b"1001")

    def download(self, args):
        """实现文件下载"""
        file_path = os.path.normpath(os.path.join(self.server_download_dir, args['filename']))
        if not os.path.exists(file_path):
            print('file:%s is not exists' % file_path)
            return
        else:
            filesize = os.path.getsize(file_path)
        filesize_struct = struct.pack('i', filesize)
        # 发送服务端文件的大小
        self.request.send(filesize_struct)
        send_size = 0  # 记录读取文件的大小
        file_md5 = hashlib.md5()  # 计算文件的md5值,保证上传的完整性
        # 接下来传输文件
        with open(file_path, "rb") as f:
            for line in f:
                file_md5.update(line)
                self.request.send(line)
                send_size += len(line)
        # 发送文件的md5,判断文件一致性
        file_md5_code = file_md5.hexdigest()  # 获取文件md5的值,字符串形式
        file_md5_client = self.request.recv(self.max_packet_size).decode(self.coding)
        if file_md5_client == file_md5_code:
            print("%s文件下载成功,大小为%s字节!" % (args['filename'], filesize))
            self.request.send(b"1000")
        else:
            self.request.send(b"1001")



if __name__ == '__main__':
    ip_port = ('127.0.0.1', 9999)
    server = socketserver.ThreadingTCPServer(ip_port, MyServer)
    server.serve_forever()

客户端代码:

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: Xiaobai Lei
import socket
import subprocess
import os
import json
import hashlib
import struct


class MyTcpClient:
    address_family = socket.AF_INET
    socket_type = socket.SOCK_STREAM
    allow_reuse_address = False
    max_packet_size = 8192
    coding = 'utf-8'
    request_queue_size = 5
    # client_download_dir = 'file_download'

    def __init__(self, server_address, connect=True):
        self.server_address = server_address
        self.socket = socket.socket(self.address_family,
                                    self.socket_type)
        if connect:
            try:
                self.client_connect()
            except:
                self.client_close()
                raise

    def client_connect(self):
        self.socket.connect(self.server_address)

    def client_close(self):
        self.socket.close()

    def run(self):
        while True:
            cmd = input(">>: ").strip()
            if not cmd:continue
            action,filename = cmd.split()
            if hasattr(self, action):
                func = getattr(self, action)
                func((action,filename))

    def put(self, args):
        """实现文件上传"""
        action, filename = args[0],args[1]
        if not os.path.isfile(filename):
            print('file:%s is not exists' % filename)
            return
        else:
            filesize = os.path.getsize(filename)
        head_dic = {'action': action, 'filename': os.path.basename(filename), 'filesize': filesize}
        head_json = json.dumps(head_dic)
        head_json_bytes = head_json.encode('utf8')
        head_struct = struct.pack("i", len(head_json_bytes))
        # 先发送字典数据的pack字节
        self.socket.send(head_struct)
        # 接着发送字典数据
        self.socket.send(head_json_bytes)
        send_size = 0
        file_md5 = hashlib.md5()
        # 最后发送需要传输的文件
        with open(filename, 'rb') as f:
            for line in f:
                file_md5.update(line)
                self.socket.send(line)
                send_size += len(line)
        # 发送文件的md5,判断文件一致性
        file_md5_code = file_md5.hexdigest()    # 获取文件md5的值,字符串形式
        self.socket.recv(self.max_packet_size)
        self.socket.send(file_md5_code.encode(self.coding)) # 发送文件的md5值给服务端
        code = self.socket.recv(self.max_packet_size).decode(self.coding)
        if code == "1000":
            print("%s字节的%s文件上传成功!"%(send_size, filename))
        else:
            print("上传失败,请重新上传!")

    def download(self, args):
        """实现文件下载"""
        print(args)
        action, filename = args[0], args[1]
        head_dic = {'action': action, 'filename': os.path.basename(filename)}
        head_json = json.dumps(head_dic)
        head_json_bytes = head_json.encode('utf8')
        head_struct = struct.pack("i", len(head_json_bytes))
        # 先发送字典数据的pack字节
        self.socket.send(head_struct)
        # 接着发送字典数据
        self.socket.send(head_json_bytes)
        # 接收服务端要发送过来的文件大小数据
        filesize_struct = self.socket.recv(4)
        filesize = struct.unpack('i', filesize_struct)[0]
        recv_size = 0  # 记录读取文件的大小
        file_md5 = hashlib.md5()  # 计算文件的md5值,保证上传的完整性
        with open(filename, "wb") as f:
            while recv_size < filesize:
                recv_data = self.socket.recv(self.max_packet_size)
                file_md5.update(recv_data)
                f.write(recv_data)
                recv_size += len(recv_data)
        file_md5_code = file_md5.hexdigest()  # 获取文件md5的值,字符串形式
        self.socket.send(file_md5_code.encode(self.coding)) # 发送文件的md5值给服务端
        code = self.socket.recv(self.max_packet_size).decode(self.coding)
        if code == "1000":
            print("%s字节的%s文件下载成功!"%(recv_size, filename))
        else:
            print("下载失败,请重新下载!")


if __name__ == '__main__':
    client = MyTcpClient(('127.0.0.1', 9999))
    client.run()

 

上一篇:recv和recvfrom的区别


下一篇:DAY31、socket套接字