def download_file_loc(self, input_location, file_path, part_size_kb=64, file_size=None, progress_callback=None): """Downloads media from the given input_file_location to the specified file_path. If a progress_callback function is given, it will be called taking two arguments (downloaded bytes count and total file size)""" if not part_size_kb: if not file_size: raise ValueError('A part size value must be provided') else: part_size_kb = get_appropiate_part_size(file_size) part_size = int(part_size_kb * 1024) if part_size % 1024 != 0: raise ValueError('The part size must be evenly divisible by 1024') # Ensure that we'll be able to download the media utils.ensure_parent_dir_exists(file_path) # Start with an offset index of 0 offset_index = 0 with open(file_path, 'wb') as file: while True: # The current offset equals the offset_index multiplied by the part size offset = offset_index * part_size result = self.invoke( GetFileRequest(input_location, offset, part_size)) offset_index += 1 # If we have received no data (0 bytes), the file is over # So there is nothing left to download and write if not result.bytes: return result.type # Return some extra information file.write(result.bytes) if progress_callback: progress_callback(file.tell(), file_size)
def upload_file(self, file_path, part_size_kb=None, file_name=None, progress_callback=None): """Uploads the specified file_path and returns a handle which can be later used :param file_path: The file path of the file that will be uploaded :param part_size_kb: The part size when uploading the file. None = Automatic :param file_name: The name of the uploaded file. None = Automatic :param progress_callback: A callback function which takes two parameters, uploaded size (in bytes) and total file size (in bytes) This is called every time a part is uploaded """ file_size = path.getsize(file_path) if not part_size_kb: part_size_kb = get_appropiate_part_size(file_size) 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 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)) hash_md5 = md5() with open(file_path, 'rb') as file: for part_index in range(part_count): # Read the file by in chunks of size part_size part = file.read(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(request) if result: hash_md5.update(part) if progress_callback: progress_callback(file.tell(), file_size) else: raise ValueError( 'Could not upload file part #{}'.format(part_index)) # Set a default file name if None was specified if not file_name: file_name = path.basename(file_path) # After the file has been uploaded, we can return a handle pointing to it return InputFile(id=file_id, parts=part_count, name=file_name, md5_checksum=hash_md5.hexdigest())