mirror of
https://github.com/LonamiWebs/Telethon.git
synced 2024-11-28 20:33:45 +03:00
Added ability to upload large files
This commit is contained in:
parent
922f17956b
commit
15994d0b78
|
@ -216,7 +216,7 @@ if __name__ == '__main__':
|
||||||
client.run()
|
client.run()
|
||||||
|
|
||||||
except Exception as e:
|
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:
|
finally:
|
||||||
print_title('Exit')
|
print_title('Exit')
|
||||||
|
|
|
@ -21,7 +21,6 @@ class TcpClient:
|
||||||
"""Connects to the specified IP and port number"""
|
"""Connects to the specified IP and port number"""
|
||||||
self.socket.connect((ip, port))
|
self.socket.connect((ip, port))
|
||||||
self.connected = True
|
self.connected = True
|
||||||
self.socket.setblocking(False)
|
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
"""Closes the connection"""
|
"""Closes the connection"""
|
||||||
|
@ -34,18 +33,9 @@ class TcpClient:
|
||||||
|
|
||||||
# Ensure that only one thread can send data at once
|
# Ensure that only one thread can send data at once
|
||||||
with self.lock:
|
with self.lock:
|
||||||
# Do not use .sendall:
|
# Set blocking so it doesn't error
|
||||||
# "on error, an exception is raised, and there is no way to
|
self.socket.setblocking(True)
|
||||||
# determine how much data, if any, was successfully sent."
|
self.socket.sendall(data)
|
||||||
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
|
|
||||||
|
|
||||||
def read(self, buffer_size):
|
def read(self, buffer_size):
|
||||||
"""Reads (receives) the specified bytes from the connected peer"""
|
"""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
|
# Ensure it is not cancelled at first, so we can enter the loop
|
||||||
self.cancelled = False
|
self.cancelled = False
|
||||||
|
|
||||||
|
# Set non-blocking so it can be cancelled
|
||||||
|
self.socket.setblocking(False)
|
||||||
|
|
||||||
with BinaryWriter() as writer:
|
with BinaryWriter() as writer:
|
||||||
while writer.written_count < buffer_size:
|
while writer.written_count < buffer_size:
|
||||||
# Only do cancel if no data was read yet
|
# Only do cancel if no data was read yet
|
||||||
|
|
|
@ -16,6 +16,7 @@ from tl import MTProtoRequest
|
||||||
from tl import Session
|
from tl import Session
|
||||||
|
|
||||||
# The Requests and types that we'll be using
|
# The Requests and types that we'll be using
|
||||||
|
from tl.functions.upload import SaveBigFilePartRequest
|
||||||
from tl.types import \
|
from tl.types import \
|
||||||
PeerUser, PeerChat, PeerChannel, \
|
PeerUser, PeerChat, PeerChannel, \
|
||||||
InputPeerUser, InputPeerChat, InputPeerChannel, InputPeerEmpty, \
|
InputPeerUser, InputPeerChat, InputPeerChannel, InputPeerEmpty, \
|
||||||
|
@ -270,35 +271,41 @@ class TelegramClient:
|
||||||
|
|
||||||
def upload_file(self, file_path, part_size_kb=64, file_name=None):
|
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.
|
"""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.
|
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 part_size_kb > 512:
|
||||||
|
raise ValueError('The part size must be less or equal to 512KB')
|
||||||
|
|
||||||
part_size = int(part_size_kb * 1024)
|
part_size = int(part_size_kb * 1024)
|
||||||
if part_size % 1024 != 0:
|
if part_size % 1024 != 0:
|
||||||
raise ValueError('The part size must be evenly divisible by 1024')
|
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
|
# Multiply the datetime timestamp by 10^6 to get the ticks
|
||||||
# This is high likely going to be unique
|
# This is high likely going to be unique
|
||||||
file_id = int(datetime.now().timestamp() * (10 ** 6))
|
file_id = int(datetime.now().timestamp() * (10 ** 6))
|
||||||
part_index = 0
|
|
||||||
hash_md5 = md5()
|
hash_md5 = md5()
|
||||||
|
|
||||||
with open(file_path, 'rb') as file:
|
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
|
# Read the file by in chunks of size part_size
|
||||||
part = file.read(part_size)
|
part = file.read(part_size)
|
||||||
|
|
||||||
# If we have read no data (0 bytes), the file is over
|
# The SavePartRequest is different depending on whether
|
||||||
# So there is nothing left to upload
|
# the file is too large or not (over or less than 10MB)
|
||||||
if not part:
|
if is_large:
|
||||||
break
|
request = SaveBigFilePartRequest(file_id, part_index, part_count, part)
|
||||||
|
else:
|
||||||
print('I read {} out of {}'.format(len(part), part_size))
|
request = SaveFilePartRequest(file_id, part_index, part)
|
||||||
|
|
||||||
# Invoke the file upload and increment both the part index and MD5 checksum
|
# 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:
|
if result:
|
||||||
part_index += 1
|
|
||||||
hash_md5.update(part)
|
hash_md5.update(part)
|
||||||
else:
|
else:
|
||||||
raise ValueError('Could not upload file part #{}'.format(part_index))
|
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
|
# After the file has been uploaded, we can return a handle pointing to it
|
||||||
return InputFile(id=file_id,
|
return InputFile(id=file_id,
|
||||||
parts=part_index,
|
parts=part_count,
|
||||||
name=file_name,
|
name=file_name,
|
||||||
md5_checksum=hash_md5.hexdigest())
|
md5_checksum=hash_md5.hexdigest())
|
||||||
|
|
||||||
|
@ -331,6 +338,10 @@ class TelegramClient:
|
||||||
# TODO If the input file is an audio, find out:
|
# TODO If the input file is an audio, find out:
|
||||||
# Performer and song title and add DocumentAttributeAudio
|
# 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,
|
self.send_media_file(InputMediaUploadedDocument(file=input_file,
|
||||||
mime_type=mime_type,
|
mime_type=mime_type,
|
||||||
attributes=attributes,
|
attributes=attributes,
|
||||||
|
|
Loading…
Reference in New Issue
Block a user