紧接上文远程唤醒计算机利用Python传输文件并自动进行AE渲染(一)前期各项配置,远程唤醒计算机利用Python传输文件并自动进行AE渲染(一)前期各项配置的内容。
本文涉及的库:
代码分为客户端与服务端,由于本文代码较多较繁杂,所以我将客户端与服务端的完整代码直接全部粘贴上来。注释部分包括文内注释与文末注释。
#服务端
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()
#客户端
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)
远程唤醒功能使用的是wolcmd.exe完成,基于上一篇文章的配置完成后,在客户端运行wolcmd.exe并输入参数便可以远程唤醒服务端PC,在服务端PC中新建批处理文件启动服务端py文件,并将bat文件放入开机自启文件夹中即可。
在使用Socket发送头文件时,由于限定的最大buffer为1024,所以在传输过程中会出现传输部分无意义内容,即bytes中的\x00(空格),而在filepath里如果存在空格,那么在运行with open的时候会报错:valueError Embedded null 。所以在这段需要替换字符串中的/x00内容。
同样在socket传输文件时,许多人会将这行写成 recved_size += recv_size , 以此来表达“已经接受文件大小加上刚刚接受的文件大小”的意思,但是在实际文件传输中,recv_size定义的仅仅是本轮文件传输的最大size,而不是实际传输的size。 如果写成recved_size += recv_size 会有一定几率导致文件还没有传输完毕就断开连接, 经过MD5检验便可以发现错误。 所以此处应写为recved_size += len(info),表示实际传输的大小。
这里利用AE在渲染文件时,渲染的输出视频文件大小会实时增加的特性,以文件大小有没有变化判定工程是否转码完成。