You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 15:17:09