Added ability to upload large files

This commit is contained in:
Lonami 2016-09-16 16:37:45 +02:00
parent 922f17956b
commit 15994d0b78
3 changed files with 31 additions and 27 deletions

View File

@ -216,7 +216,7 @@ if __name__ == '__main__':
client.run()
except Exception as e:
print('Unexpected error ({}): {} at\n{}', type(e), e, traceback.format_exc())
print('Unexpected error ({}): {} at\n{}'.format(type(e), e, traceback.format_exc()))
finally:
print_title('Exit')

View File

@ -21,7 +21,6 @@ class TcpClient:
"""Connects to the specified IP and port number"""
self.socket.connect((ip, port))
self.connected = True
self.socket.setblocking(False)
def close(self):
"""Closes the connection"""
@ -34,18 +33,9 @@ class TcpClient:
# Ensure that only one thread can send data at once
with self.lock:
# Do not use .sendall:
# "on error, an exception is raised, and there is no way to
# determine how much data, if any, was successfully sent."
while data:
try:
sent = self.socket.send(data)
data = data[sent:]
except BlockingIOError as e:
if 'Errno 11' in str(e): # Error #11: Resource temporary unavailable
time.sleep(0.1) # Sleep a bit waiting for the resource to be available
else:
raise e
# Set blocking so it doesn't error
self.socket.setblocking(True)
self.socket.sendall(data)
def read(self, buffer_size):
"""Reads (receives) the specified bytes from the connected peer"""
@ -55,6 +45,9 @@ class TcpClient:
# Ensure it is not cancelled at first, so we can enter the loop
self.cancelled = False
# Set non-blocking so it can be cancelled
self.socket.setblocking(False)
with BinaryWriter() as writer:
while writer.written_count < buffer_size:
# Only do cancel if no data was read yet

View File

@ -16,6 +16,7 @@ from tl import MTProtoRequest
from tl import Session
# The Requests and types that we'll be using
from tl.functions.upload import SaveBigFilePartRequest
from tl.types import \
PeerUser, PeerChat, PeerChannel, \
InputPeerUser, InputPeerChat, InputPeerChannel, InputPeerEmpty, \
@ -270,35 +271,41 @@ class TelegramClient:
def upload_file(self, file_path, part_size_kb=64, file_name=None):
"""Uploads the specified media with the given chunk (part) size, in KB.
If no custom file name is specified, the real file name will be used.
This method will fail when trying to upload files larger than 10MB!"""
If no custom file name is specified, the real file name will be used"""
if part_size_kb > 512:
raise ValueError('The part size must be less or equal to 512KB')
part_size = int(part_size_kb * 1024)
if part_size % 1024 != 0:
raise ValueError('The part size must be evenly divisible by 1024')
# Determine whether the file is too big (over 10MB) or not
# Telegram does make a distinction between smaller or larger files
file_size = path.getsize(file_path)
is_large = file_size > 10 * 1024 * 1024
part_count = (file_size + part_size - 1) // part_size
# Multiply the datetime timestamp by 10^6 to get the ticks
# This is high likely going to be unique
file_id = int(datetime.now().timestamp() * (10 ** 6))
part_index = 0
hash_md5 = md5()
with open(file_path, 'rb') as file:
while True:
for part_index in range(part_count):
# Read the file by in chunks of size part_size
part = file.read(part_size)
# If we have read no data (0 bytes), the file is over
# So there is nothing left to upload
if not part:
break
print('I read {} out of {}'.format(len(part), part_size))
# The SavePartRequest is different depending on whether
# the file is too large or not (over or less than 10MB)
if is_large:
request = SaveBigFilePartRequest(file_id, part_index, part_count, part)
else:
request = SaveFilePartRequest(file_id, part_index, part)
# Invoke the file upload and increment both the part index and MD5 checksum
result = self.invoke(SaveFilePartRequest(file_id, part_index, part))
result = self.invoke(request)
if result:
part_index += 1
hash_md5.update(part)
else:
raise ValueError('Could not upload file part #{}'.format(part_index))
@ -309,7 +316,7 @@ class TelegramClient:
# After the file has been uploaded, we can return a handle pointing to it
return InputFile(id=file_id,
parts=part_index,
parts=part_count,
name=file_name,
md5_checksum=hash_md5.hexdigest())
@ -331,6 +338,10 @@ class TelegramClient:
# TODO If the input file is an audio, find out:
# Performer and song title and add DocumentAttributeAudio
]
# Ensure we have a mime type, any; but it cannot be None
# «The "octet-stream" subtype is used to indicate that a body contains arbitrary binary data.»
if not mime_type:
mime_type = 'application/octet-stream'
self.send_media_file(InputMediaUploadedDocument(file=input_file,
mime_type=mime_type,
attributes=attributes,