mirror of
				https://github.com/LonamiWebs/Telethon.git
				synced 2025-10-31 07:57:38 +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