远程唤醒计算机利用Python传输文件并自动进行AE渲染(二)代码实现

远程唤醒计算机利用Python传输文件并自动进行AE渲染(二)代码实现

  • 前言
  • 一、完整代码
    • 1.服务端
    • 2.客户端
  • 二、注释
      • 注释0
      • 注释1
      • 注释2
      • 注释3

前言

紧接上文远程唤醒计算机利用Python传输文件并自动进行AE渲染(一)前期各项配置,远程唤醒计算机利用Python传输文件并自动进行AE渲染(一)前期各项配置的内容。
本文涉及的库:

  1. Socket库建立两台主机网络连接
  2. Struct库对文件传输的头文件进行pack与unpack
  3. hashlib库在文件传输结束后进行md5检验文件完整性
  4. time和os库实现必要条件

代码分为客户端与服务端,由于本文代码较多较繁杂,所以我将客户端与服务端的完整代码直接全部粘贴上来。注释部分包括文内注释与文末注释。

一、完整代码

1.服务端

#服务端
import socket
import struct
import hashlib
import os
import time

class server():
    def __init__(self):
        self.host = '127.0.0.1'  			
        self.port = 6666			#若端口有占用情况可以选择其他端口
        self.head_struct = '128sq32s'		#定义头文件的结构
        self.info_size = struct.calcsize(self.head_struct)

    def get_md5(self,path):
        buffer_size = 1024
        file_size = os.path.getsize(path)
        md5 = hashlib.md5()
        readed_size = 0
        with open(path, 'rb') as f:
            while readed_size < file_size:
                remain_size = file_size - readed_size
                read_size = buffer_size if buffer_size < remain_size else remain_size
                readed_size += read_size
                md5.update(f.read(read_size))
        md = md5.hexdigest()
        return md

    def get_value(self,path):
        file_name = os.path.basename(path)
        file_size = os.path.getsize(path)
        md = self.get_md5(path)
        return file_name, file_size, md


    def unpack_recv(self,recv_info):
        file_name, file_size, md5 = struct.unpack(self.head_struct, recv_info)
        return file_name, file_size, md5

    def check_md5(self,path):
        buffer_size = 1024
        file_size = os.path.getsize(path)
        md5 = hashlib.md5()
        readed_size = 0
        with open(path, 'rb') as f:
            while readed_size < file_size:
                remain_size = file_size - readed_size
                read_size = buffer_size if buffer_size < remain_size else remain_size
                readed_size += read_size
                md5.update(f.read(read_size))
        md = md5.hexdigest()
        return md


    def send_file(self,conn,path):					#回传渲染好的视频文件
        buffer_size = 1024
        file_name, file_size, md = self.get_value(path)
        struct_info = struct.pack(self.head_struct, bytes(file_name, encoding='utf-8'), file_size,
                                  bytes(md, encoding='utf-8'))
        conn.send(struct_info)
        sended_size = 0
        with open(path, 'rb') as f:
            while sended_size < file_size:
                remain_size = file_size - sended_size
                print("传输文件剩余大小:" + str(remain_size))
                send_size = buffer_size if buffer_size < remain_size else buffer_size
                send_info = f.read(send_size)
                conn.send(send_info)
                sended_size += len(send_info)
        print('文件传输结束', md)
        result = conn.recv(1024)
        print(result.decode("utf-8"))
        if "成功" in result.decode("utf-8"):
            print("发送成功")
            conn.close()				
			os.system('shutdown /s /t 0')		#文件发送成功断开连接并立即关机
        else:
            conn.close()


    def check_file(self,conn):
        path = "G:\\AfterEffectShare\\OutputFiles"	#此处修改为你用来保存渲染视频文件的文件夹
        dirs = os.listdir(path)
        i = 0
        text_list = []
        for file in dirs:
            text = "{}、{}".format(i,file)
            text_list.append(text)
            i += 1
        warn = "请输入您当前工程输出文件的序号"
        text_list.append(warn)
        text_list = "\n".join(text_list)
        conn.send(text_list.encode("utf-8"))
        # num = int(input("请输入您当前工程项目的序号"))
        temp_num = conn.recv(1024)
        num = int(temp_num.decode("utf-8"))
        file = dirs[num]
        file_path = "{}\\{}".format(path, file)
        while True:									################详见注释3##############
            fsize = os.path.getsize(file_path)
            temp = fsize
            time.sleep(5)
            fsize = os.path.getsize(file_path)
            if fsize == temp:
                message = "工程文件转码结束,现在开始回传文件".encode("utf-8")
                conn.send(message)
                self.send_file(conn,file_path)
                break
            else:
                message = "正在转码....现文件大小{}".format(fsize).encode("utf-8")
                conn.send(message)


    def file_recv(self):
        buffer_size = 1024
        path = "G:\AfterEffectShare\ProjectFiles" 		#更改为你用来保存接受到的工程文件的文件夹
        sk = socket.socket()
        server_address = (self.host, self.port)
        sk.settimeout(120)								#120秒无连接后自动关闭,避免开机自启占用内存
        sk.bind(server_address)
        sk.listen(3)
        conn, addr = sk.accept()
        print("IP地址:" + addr[0] + "连接成功")
        recv_info = conn.recv(self.info_size)  		#接受客户端发送的头文件,客户端main函数第七行
        file_name, file_size, md5_info = self.unpack_recv(recv_info) #unpack头文件
        print("文件名:"+file_name.decode("utf-8"))
        print("文件大小:"+str(file_size))
        print("文件MD5:"+md5_info.decode("utf-8"))	
        recved_size = 0
        path = "{}\{}".format(path,file_name.decode("utf-8"))
        path = path.replace("\x00","")			################见文末注释1##################
        print("开始传输文件" + path)
        with open(path, 'wb') as f:
            while recved_size < file_size:
                remain_size = file_size - recved_size	#开始传输二进制内容
                recv_size = buffer_size if buffer_size < remain_size else remain_size
                info = conn.recv(recv_size)
                recved_size += len(info)		################见文末注释2###################
                print("剩余大小"+ str(file_size - recved_size))
                f.write(info)
        md5 = self.check_md5(path)				#生成MD5值,并与头文件中的md5_info比对
        if md5 == md5_info.decode('utf-8'):
            print("传输成功,MD5正常")
            print('保存路径'+ path)
            y = "文件传输成功,MD5检验正确".encode("utf-8")
            conn.send(y)
            self.check_file(conn)				#检验成功后check_file函数
        else:
            print("传输失败,MD5错误")
            n = "文件传输错误,MD5检验失败".encode("utf-8")
            conn.send(n)
            conn.close()
            sk.close()

if __name__ == '__main__':
    serv = server()
    serv.file_recv()


2.客户端

#客户端
import socket
import struct
import os
import hashlib
import time
from tkinter import filedialog


host = '192.168.123.2'		#此处修改为在第一章中准备好的域名或固定IP
port = 6666 				#跟随服务端端口
path = ''
savepath = ""
head_struct = '128sq32s'
buffer_size = 1024
class client():
    def get_md5(self,path):
        file_size = os.path.getsize(path)
        md5 = hashlib.md5()
        readed_size = 0
        with open(path, 'rb') as f:
            while readed_size < file_size:
                remain_size = file_size - readed_size
                read_size = buffer_size if buffer_size < remain_size else remain_size
                readed_size += read_size
                md5.update(f.read(read_size))
        md = md5.hexdigest()
        return md


    def unpack_recv(self,recv_info):
        file_name, file_size, md5 = struct.unpack(head_struct, recv_info)
        return file_name, file_size, md5


    def get_value(self,path):
        file_name = os.path.basename(path)
        file_size = os.path.getsize(path)
        md = self.get_md5(path)
        return file_name, file_size, md


    def check_md5(self,path):
        buffer_size = 1024
        file_size = os.path.getsize(path)
        md5 = hashlib.md5()
        readed_size = 0
        with open(path, 'rb') as f:
            while readed_size < file_size:
                remain_size = file_size - readed_size
                read_size = buffer_size if buffer_size < remain_size else remain_size
                long = md5.update(f.read(read_size))
                readed_size += read_size
        md = md5.hexdigest()
        return md


    def get_file(self,sk):
        global savapath
        head_strut = '128sq32s'
        buffer_size = 1024
        info_size = struct.calcsize(head_strut)
        recv_info = sk.recv(info_size)  # 接收长度信息
        file_name, file_size, md5_info = self.unpack_recv(recv_info)
        print("文件名:" + file_name.decode("utf-8"))
        print("文件大小:" + str(file_size))
        print("文件MD5:" + md5_info.decode("utf-8"))
        savepath2 = "{}\{}".format(savepath, file_name.decode("utf-8"))
        savepath2 = savepath2.replace("\x00", "")
        recved_size = 0
        with open(savepath2, 'wb') as f:
            while recved_size < file_size:
                remain_size = file_size - recved_size
                recv_size = buffer_size if buffer_size < remain_size else remain_size
                info = sk.recv(recv_size)
                recved_size += len(info)
                print("剩余大小"+ str(file_size - recved_size))
                f.write(info)
        md5 = self.check_md5(path)
        if md5 == md5_info.decode('utf-8'):
            print("传输成功,MD5正常")
            print('保存路径' + path)
            y = "文件传输成功,MD5检验正确".encode("utf-8")
            sk.send(y)
            sk.close()
        else:
            print("传输失败,MD5错误")
            n = "文件传输错误,MD5检验失败".encode("utf-8")
            sk.send(n)
            sk.close()
    def wait_process(self,sk):
        time.sleep(3)
        temp = sk.recv(1024)
        print(len(temp))
        print(temp.decode("utf-8"))
        num = input()
        num = num.encode("utf-8")
        sk.send(num)
        print("正在读取文件大小")
        time.sleep(3)
        while True:
            text = sk.recv(1024)
            print(text.decode("utf-8"))
            time.sleep(5)
            if "转码结束" in text.decode("utf-8"):
                self.get_file(sk)
                break
            else:
                print()


    def main(self,path):
        sk = socket.socket()
        sk.connect((host, port))
        print("连接成功")
        file_name, file_size, md = self.get_value(path)
        struct_info = struct.pack(head_struct, bytes(file_name, encoding='utf-8'), file_size, bytes(md, encoding='utf-8'))
        sk.send(struct_info)			#发送struct.pack后的头文件
        sended_size = 0
        with open(path, 'rb') as f:
            while sended_size < file_size:
                remain_size = file_size - sended_size
                print("传输文件剩余大小:" + str(remain_size))
                send_size = buffer_size if buffer_size < remain_size else buffer_size
                send_info = f.read(send_size)
                sk.send(send_info)
                sended_size += len(send_info)
            print('文件传输结束', md)
        result = sk.recv(1024)
        print(result.decode("utf-8"))
        if "成功" in result.decode("utf-8"):
            print("读取成功")
            self.wait_process(sk)
        else:
            sk.close()
            
if __name__ == '__main__':
    path = filedialog.askopenfilename(title="请选择您要上传的文件")
    savapath = filedialog.askdirectory(title="请选择您要保存文件的文件夹")
    cli = client()
    cli.main(path)

二、注释

注释0

远程唤醒功能使用的是wolcmd.exe完成,基于上一篇文章的配置完成后,在客户端运行wolcmd.exe并输入参数便可以远程唤醒服务端PC,在服务端PC中新建批处理文件启动服务端py文件,并将bat文件放入开机自启文件夹中即可。

注释1

在使用Socket发送头文件时,由于限定的最大buffer为1024,所以在传输过程中会出现传输部分无意义内容,即bytes中的\x00(空格),而在filepath里如果存在空格,那么在运行with open的时候会报错:valueError Embedded null 。所以在这段需要替换字符串中的/x00内容。

注释2

同样在socket传输文件时,许多人会将这行写成 recved_size += recv_size , 以此来表达“已经接受文件大小加上刚刚接受的文件大小”的意思,但是在实际文件传输中,recv_size定义的仅仅是本轮文件传输的最大size,而不是实际传输的size。 如果写成recved_size += recv_size 会有一定几率导致文件还没有传输完毕就断开连接, 经过MD5检验便可以发现错误。 所以此处应写为recved_size += len(info),表示实际传输的大小。

注释3

这里利用AE在渲染文件时,渲染的输出视频文件大小会实时增加的特性,以文件大小有没有变化判定工程是否转码完成。

你可能感兴趣的:(python)