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

Telegram Bot图片存储路径异常问题排查求助

Telegram Bot 图片存储异常问题解决

问题现象

用户点击send_mem按钮后,后续无论点击send_mem还是send_motivator按钮,发送的图片都会保存到/mem文件夹,无法按选择的按钮保存到对应目录。

问题根源

原代码中,picture_saver函数每次被调用时都会重复注册同一个@dp.message_handler(content_types=ContentType.PHOTO)处理器。Aiogram的Dispatcher会按处理器注册顺序匹配请求,第一次调用picture_saver("photo/mem")后,这个处理器就被固定绑定到了"photo/mem"路径,后续再调用picture_saver("photo/motivator")注册的新处理器不会生效(因为第一个处理器已经先匹配到图片消息)。

这种动态注册处理器的方式完全错误,正确的做法是用**状态机(FSM)**记录用户当前选择的保存路径,再根据状态处理后续的图片上传。

修复方案

使用Aiogram的FSM(有限状态机)跟踪用户操作状态,完整修复代码如下:

import logging
import os
import random

from aiogram import Bot, Dispatcher, types
from aiogram.contrib.fsm_storage.memory import MemoryStorage
from aiogram.types import ReplyKeyboardMarkup, KeyboardButton, ContentType
from aiogram.utils import executor
from aiogram.dispatcher import FSMContext
from aiogram.dispatcher.filters.state import State, StatesGroup

# 设置日志级别
logging.basicConfig(level=logging.INFO)

# 初始化bot和dispatcher
bot = Bot(token='xxxxxx')
dp = Dispatcher(bot, storage=MemoryStorage())

# 定义状态类,保存用户当前选择的保存路径
class SavePhotoState(StatesGroup):
    waiting_for_photo = State()
    save_path = State()

# 随机获取图片函数
def random_image(path):
    folder = path
    files = os.listdir(folder)
    paths = [os.path.join(folder, file) for file in files]
    random_path = random.choice(paths)
    image = open(random_path, 'rb')
    return image

# /start命令处理器
@dp.message_handler(commands=['start'])
async def start_command(message: types.Message):
    show_mem = KeyboardButton('show_mem')
    show_motivator = KeyboardButton('show_motivator')
    show_art = KeyboardButton('show_art')
    send_mem = KeyboardButton('send_mem')
    send_motivator = KeyboardButton('send_motivator')
    keyboard = ReplyKeyboardMarkup(resize_keyboard=True, one_time_keyboard=True).add(
        show_mem, show_motivator, show_art, send_mem, send_motivator
    )
    await bot.send_message(message.from_user.id, 'Hey!', reply_markup=keyboard)

# 文本按钮处理器
@dp.message_handler(content_types=['text'])
async def button_processing(message: types.Message, state: FSMContext):
    if message.text == 'show_mem':
        await bot.send_photo(message.from_user.id, photo=random_image("photo/mem"))
    elif message.text == 'show_motivator':
        await bot.send_photo(message.from_user.id, photo=random_image("photo/motivator"))
    elif message.text == 'show_art':
        await bot.send_photo(message.from_user.id, photo=random_image("photo/photo"))
    elif message.text == 'send_mem':
        # 设置状态和保存路径
        await state.update_data(save_path="photo/mem")
        await SavePhotoState.waiting_for_photo.set()
        await message.answer("请发送要保存的图片")
    elif message.text == 'send_motivator':
        await state.update_data(save_path="photo/motivator")
        await SavePhotoState.waiting_for_photo.set()
        await message.answer("请发送要保存的图片")

# 图片处理器,仅在等待图片状态时触发
@dp.message_handler(content_types=ContentType.PHOTO, state=SavePhotoState.waiting_for_photo)
async def save_photo(message: types.Message, state: FSMContext):
    # 获取之前保存的路径
    data = await state.get_data()
    save_path = data.get("save_path")
    
    photo = message.photo[-1]
    file = await photo.get_file()
    # 修复原代码中message.text为None的问题,改用message_id作为文件名
    filename = os.path.join(save_path, f"{message.message_id}.jpg")
    await bot.download_file(file.file_path, filename)
    await message.answer("Фото сохранено!")
    
    # 结束状态,重置为初始状态
    await state.finish()

if __name__ == '__main__':
    executor.start_polling(dp, skip_updates=True)

关键改进点

  • 用StatesGroup定义状态,跟踪用户是否处于等待上传图片的状态
  • 点击send_mem/send_motivator时,通过state.update_data保存目标路径,并切换到等待图片的状态
  • 图片处理器通过state=SavePhotoState.waiting_for_photo过滤,只处理处于该状态的用户发送的图片
  • 修复了原代码中message.text在图片消息中为None的错误(原代码会生成无效文件名)
  • 处理完图片后调用await state.finish()重置状态,避免影响后续操作

内容的提问来源于stack exchange,提问作者DEAD END TV

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 07:42:41