aiogram中替换用户已上传文件时回调处理器download方法报错求助
解决aiogram回调处理器中无法下载文件的AttributeError问题
问题场景
用户上传PDF文件到机器人,文件成功存入指定文件夹后,若再次上传同名但内容不同的PDF文件,程序会询问用户是否替换原文件。但在确认替换的回调处理器中,执行await callback.message.document.download(destination)时触发AttributeError: 'NoneType' object has no attribute 'download'错误。
错误回溯
File "D:\Python\Work_helpers\Bilight_SQL\bot.py", line 444, in callback_scan_yes await callback.message.document.download(destination) AttributeError: 'NoneType' object has no attribute 'download'
原代码片段
消息处理器(处理文件上传)
@dp.message_handler(content_types=types.ContentType.ANY, state=FSMAdmin.scan_upload) async def load_scan_to_postgresql(message: types.Message, state: FSMContext): if message.content_type != 'document': await FSMAdmin.scan_upload.set() await message.answer('Загружать файлы можно только в формате .pdf', reply_markup=markup_back) else: file_extension = '.' + message.document.file_name.split('.')[-1] if file_extension != '.pdf': await FSMAdmin.scan_upload.set() await message.answer("Документ должен быть в формате .pdf", reply_markup=markup_back) else: doc = message.document.file_name destination = rf"certificates\{doc}" async with state.proxy() as data: data['doc'] = doc if doc in os.listdir(f"certificates"): await FSMAdmin.scan_upload.set() await message.reply(f"ФАЙЛ С ТАКИМ ИМЕНЕМ УЖЕ ЗАГРУЖЕН В БАЗУ," f"ХОТИТЕ ЗАМЕНИТЬ НА НОВЫЙ?", reply_markup=markup_scan) else: await message.document.download(destination_file=destination) await message.reply(f"НОВЫЙ СКАН СЕРТИФИКАТА ЗАГРУЖЕН В БАЗУ", reply_markup=markup_back) await state.finish()
确认替换的回调处理器
@dp.callback_query_handler(text='replace_scan_yes', state=[FSMAdmin.scan_upload]) async def callback_scan_yes(callback: types.CallbackQuery, state=FSMContext): async with state.proxy() as data: doc = data['doc'] destination = rf"certificates\{doc}" await callback.message.document.download(destination) await FSMAdmin.scan_upload.set() await callback.message.reply(f"ЗАГРУЗКА ПРОШЛА УСПЕШНО,СКАН СЕРТИФИКАТА В БАЗЕ" f"ЗАМЕНЕН НА НОВЫЙ", reply_markup=markup_back) await state.finish()
取消替换的回调处理器
@dp.callback_query_handler(text='replace_scan_no', state=[FSMAdmin.scan_upload]) async def callback_scan_no(callback: types.CallbackQuery, state=FSMContext): await FSMAdmin.scan_upload.set() await callback.message.reply(f"ЗАГРУЗКА ОТМЕНЕНА,СКАН СЕРТИФИКАТА В БАЗЕ ОСТАЛСЯ БЕЗ ИЗМЕНЕНИЙ", reply_markup=markup_back) await state.finish()
问题原因
callback.message指向的是机器人发送的那条"是否替换文件"的回复消息,这条消息本身不包含任何文件,所以callback.message.document的值为None,调用download()方法自然会触发AttributeError。用户上传的新文件实际存在于最初发送的那条包含文件的消息中,我们需要保存该文件的唯一标识file_id,才能在回调中重新获取并下载。
解决方案
步骤1:在消息处理器中保存文件的file_id
修改消息处理器中保存state的部分,除了文件名,额外保存用户上传文件的file_id:
async with state.proxy() as data: data['doc'] = doc data['file_id'] = message.document.file_id # 新增:保存文件的唯一标识
步骤2:在回调处理器中通过file_id下载文件
在确认替换的回调中,从state取出file_id,通过bot.get_file()获取文件对象,再调用bot.download_file()完成下载:
@dp.callback_query_handler(text='replace_scan_yes', state=[FSMAdmin.scan_upload]) async def callback_scan_yes(callback: types.CallbackQuery, state=FSMContext): async with state.proxy() as data: doc = data['doc'] file_id = data['file_id'] # 取出保存的file_id destination = rf"certificates\{doc}" # 通过file_id获取文件并下载 file = await callback.bot.get_file(file_id) await callback.bot.download_file(file.file_path, destination) await callback.message.reply(f"ЗАГРУЗКА ПРОШЛА УСПЕШНО,СКАН СЕРТИФИКАТА В БАЗЕ" f"ЗАМЕНЕН НА НОВЫЙ", reply_markup=markup_back) await state.finish()
说明
file_id是Telegram平台给每个文件分配的唯一标识,只要文件未被删除,就能通过该ID获取文件callback.bot可以直接获取机器人实例,无需额外导入- 下载完成后不需要再调用
await FSMAdmin.scan_upload.set(),因为后续已经执行await state.finish()结束状态
内容的提问来源于stack exchange,提问作者Gorgeous George
相关产品推荐
相关产品推荐

