以下是使用 Python 实现 STEP 协议进行文件上传的代码示例:

import argparse
import hashlib
import os
import struct
import time
import uuid
from socket import *
import json

MAX_PACKET_SIZE = 20480

# Const Value
OP_SAVE, OP_DELETE, OP_GET, OP_UPLOAD, OP_DOWNLOAD, OP_BYE, OP_LOGIN, OP_ERROR = 'SAVE', 'DELETE', 'GET', 'UPLOAD', 'DOWNLOAD', 'BYE', 'LOGIN', 'ERROR'
TYPE_FILE, TYPE_DATA, TYPE_AUTH, DIR_EARTH = 'FILE', 'DATA', 'AUTH', 'EARTH'
FIELD_OPERATION, FIELD_DIRECTION, FIELD_TYPE, FIELD_USERNAME, FIELD_PASSWORD, FIELD_TOKEN = 'operation', 'direction', 'type', 'username', 'password', 'token'
FIELD_KEY, FIELD_SIZE, FIELD_TOTAL_BLOCK, FIELD_MD5, FIELD_BLOCK_SIZE = 'key', 'size', 'total_block', 'md5', 'block_size'
FIELD_STATUS, FIELD_STATUS_MSG, FIELD_BLOCK_INDEX = 'status', 'status_msg', 'block_index'
DIR_REQUEST, DIR_RESPONSE = 'REQUEST', 'RESPONSE'


def make_request_packet(operation, data_type, json_data, bin_data=None):
    '''
    Make a packet for request
    :param operation: [SAVE, DELETE, GET, UPLOAD, DOWNLOAD, BYE, LOGIN]
    :param data_type: [FILE, DATA, AUTH]
    :param json_data
    :param bin_data
    :return:
    '''
    json_data[FIELD_OPERATION] = operation
    json_data[FIELD_DIRECTION] = DIR_REQUEST
    json_data[FIELD_TYPE] = data_type
    return make_packet(json_data, bin_data)


def make_fileRequest_packet(operation, data_type, file_key, file_size, block_index, token, json_data, bin_data):
    '''
    Make a packet for fileRequest
    :param file_size:
    :param block_index:
    :param token:
    :param file_key:
    :param operation: [SAVE, DELETE, GET, UPLOAD, DOWNLOAD, BYE, LOGIN]
    :param data_type: [FILE, DATA, AUTH]
    :param json_data
    :param bin_data
    :return:
    '''
    json_data[FIELD_KEY] = file_key
    json_data[FIELD_SIZE] = file_size
    json_data[FIELD_TOKEN] = token
    json_data[FIELD_OPERATION] = operation
    json_data[FIELD_DIRECTION] = DIR_REQUEST
    json_data[FIELD_TYPE] = data_type
    json_data[FIELD_BLOCK_INDEX] = block_index
    return make_packet(json_data, bin_data)


def make_packet(json_data, bin_data=None):
    '''
    Make a packet following the STEP protocol.
    Any information or data for TCP transmission has to use this function to get the packet.
    :param json_data:
    :param bin_data:
    :return:
        The complete binary packet
    '''
    j = json.dumps(dict(json_data), ensure_ascii=False)
    j_len = len(j)
    if bin_data is None:
        return struct.pack('!II', j_len, 0) + j.encode()
    else:
        return struct.pack('!II', j_len, len(bin_data)) + j.encode() + bin_data


def get_tcp_packet(conn):
    '''
    Receive a complete TCP "packet" from a TCP stream and get the json data and binary data.
    :param conn: the TCP connection
    :return:
        json_data
        bin_data
    '''
    bin_data = b''
    while len(bin_data) < 8:
        data_rec = conn.recv(8)
        if data_rec == b'':
            time.sleep(0.01)
        if data_rec == b'':
            return None, None
        bin_data += data_rec
    data = bin_data[:8]
    bin_data = bin_data[8:]
    j_len, b_len = struct.unpack('!II', data)
    while len(bin_data) < j_len:
        data_rec = conn.recv(j_len)
        if data_rec == b'':
            time.sleep(0.01)
        if data_rec == b'':
            return None, None
        bin_data += data_rec
    j_bin = bin_data[:j_len]

    try:
        json_data = json.loads(j_bin.decode())
    except Exception as ex:
        return None, None

    bin_data = bin_data[j_len:]
    while len(bin_data) < b_len:
        data_rec = conn.recv(b_len)
        if data_rec == b'':
            time.sleep(0.01)
        if data_rec == b'':
            return None, None
        bin_data += data_rec
    return json_data, bin_data


def login(clientSocket,id):
    login_data = {
        "username": id,
        "password": hashlib.md5(id.encode()).hexdigest(),
    }
    clientSocket.send(make_request_packet(OP_LOGIN, TYPE_AUTH, login_data))
    json_data, bin_data = get_tcp_packet(clientSocket)
    return json_data


def get_file_size(file_path):
    fsize = os.path.getsize(file_path)  # 获取文件大小:以字节为单位
    return fsize


def _argparse():
    parse = argparse.ArgumentParser()
    parse.add_argument("--server_ip", default='10.7.219.224', action='store', required=False, dest="ip",
                       help="The IP address bind to the server. Default bind all IP.")
    parse.add_argument("--port", default='1379', action='store', required=False, dest="port",
                       help="The port that server listen on. Default is 1379.")
    parse.add_argument("--f", default='', action='store', required=False, dest="f",
                       help="The file path. Default is none.")
    parse.add_argument("--id", default='2037319', action='store', required=False, dest="id",
                       help="The id. Default is none.")
    return parse.parse_args()


def uploadFile(clientSocket, fileName, token):
    save_data = {}
    file_size = get_file_size(fileName)
    file_key = str(uuid.uuid4()) + os.path.splitext(fileName)[-1]
    clientSocket.send(
        make_fileRequest_packet(operation=OP_SAVE, data_type=TYPE_FILE, file_key=file_key, file_size=file_size,
                                block_index=None, token=token, json_data=save_data, bin_data=None))
    json_data, bin_data1 = get_tcp_packet(clientSocket)
    total_block = json_data["total_block"]
    block_size = MAX_PACKET_SIZE
    block_index = 0
    while block_index < total_block:
        f = open(fileName, 'rb')
        f.seek(block_size * block_index)
        bin_data = f.read(block_size)
        f.close()
        clientSocket.send(
            make_fileRequest_packet(OP_UPLOAD, TYPE_FILE, file_key, file_size, block_index, token, save_data, bin_data))
        json_data, bin_data2 = get_tcp_packet(clientSocket)
        block_index = block_index + 1
        print(json_data)
        print(type(clientSocket))

    return file_key


def get_file_md5(filename):
    '''
    Get MD5 value for big file
    :param filename:
    :return:
    '''
    m = hashlib.md5()
    with open(filename, 'rb') as fid:
        while True:
            d = fid.read(2048)
            if not d:
                break
            m.update(d)
    return m.hexdigest()


def check_file_state(clientSocket, file_key, token):
    clientSocket.send(
        make_fileRequest_packet(operation=OP_GET, data_type=TYPE_FILE, file_key=file_key, file_size=None,
                                block_index=None, token=token, json_data={}, bin_data=None))
    json_data, bin_data = get_tcp_packet(clientSocket)
    print(json_data)
    return json_data["md5"]


def main():
    parser = _argparse()
    server_ip = parser.ip
    server_port = parser.port
    file_path = parser.f
    id = parser.id
    clientSocket = socket(AF_INET, SOCK_STREAM)
    clientSocket.connect((server_ip, int(server_port)))
    json_data = login(clientSocket,id)
    token = json_data["token"]
    file_key = uploadFile(clientSocket, file_path, token)
    md5 = check_file_state(clientSocket, file_key, token)
    print("Token:", token)
    print(json_data)
    if md5 == get_file_md5(file_path):
        print("upload successfully")
    clientSocket.close()


if __name__ == '__main__':
    main()

请确保将--server_ip--port参数设置为正确的服务器IP地址和端口。另外,根据您的需求,将--f参数设置为要上传的文件的路径,并将--id参数设置为您的用户ID。

使用步骤

  1. 将代码保存为 Python 文件(例如,upload_tool.py)。
  2. 在命令行中运行以下命令,将<server_ip><port><file_path><id>替换为实际值:
python upload_tool.py --server_ip <server_ip> --port <port> --f <file_path> --id <id>
  1. 运行代码,程序将尝试连接服务器,上传文件,并进行 MD5 校验。

示例

python upload_tool.py --server_ip 192.168.1.100 --port 1379 --f /path/to/file.txt --id my_user_id

注意

  • 确保服务器端已经启动并监听指定的 IP 地址和端口。
  • 确保您已获得服务器的访问权限,并拥有有效的用户 ID 和密码。
  • MAX_PACKET_SIZE 可以根据您的网络状况调整。

代码解析

  • 代码首先定义了 STEP 协议的常量值,包括操作码、数据类型、方向等。
  • make_packet() 函数用于打包数据,将其封装成 STEP 协议规定的格式。
  • get_tcp_packet() 函数用于接收 TCP 数据包,并解析出 JSON 数据和二进制数据。
  • uploadFile() 函数负责上传文件,将文件分割成块并逐块上传。
  • get_file_md5() 函数用于计算文件的 MD5 值,用于校验传输数据的完整性。
  • check_file_state() 函数用于检查文件上传状态,并获取服务器端的 MD5 值。
  • main() 函数是程序的入口,负责解析命令行参数,连接服务器,上传文件,并进行校验。

总结

本代码示例演示了如何使用 Python 实现 STEP 协议进行文件上传,并提供 MD5 校验功能,确保文件传输完整性。您可以根据自己的需要对代码进行修改和扩展。

Python STEP 协议文件上传工具:高效传输文件

原文地址: https://www.cveoy.top/t/topic/lMT 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录