@BOSH22

Как исправить ошибку с одновременными запросами в aiogram?

Доброй ночи, возникла следующая проблема, которую никак не удаётся решить:

Есть бот на Aiogram
Основная задача - парсинг медиаконтента, его обработка и отправка обратно пользователю.

Скрипт по части парсинга выглядит так:
with open('data/config.json') as json_file:
    config = json.load(json_file)
    VK_TOKEN = config['Bot_Data']['Vk_Token']

session = vk_api.VkApi(token=VK_TOKEN)
vk = session.get_api()

def get_user_data(user_id: str) -> dict:
    data = session.method('users.get', {'user_ids': user_id, 'fields': 'sex, photo_max, followers_count'})
    data_dict = {'id': 0, 'first_name': '', 'last_name': '', 'sex': 0, 'photo': '', 'followers_count': ''}
    data_dict['id'] = data[0]['id']
    data_dict['first_name'] = data[0]['first_name']
    data_dict['last_name'] = data[0]['last_name']
    data_dict['sex'] = data[0]['sex']
    data_dict['photo'] = data[0]['photo_max']
    data_dict['followers_count'] = data[0]['followers_count']
    url = (data[0]['photo_max'])
    img = urllib.request.urlopen(url).read()
    avat = io.BytesIO()
    avat.write(img)
    with open("FIO.txt", "w") as file:
        file.write(data_dict['first_name'] + " " + data_dict['last_name'])
         
    lists = glob('dialogs/*')
    picture = random.choice(lists)
    photo222=open(picture, 'rb')
    im1 = Image.open(photo222)
    rgb_2222 = im1.convert('RGB')
    idrawf = ImageDraw.Draw(rgb_2222)
    tt = open("FIO.txt", 'r')
    txt = tt.read().rstrip()
    text = txt
    font = ImageFont.truetype("tahomabd.ttf", size=23)
    idrawf.text((192, 16), text, font=font, aling=LEFT, )   
    avat.seek(0)
    im3 = Image.open(avat)
    size=(68,68)
    im2 = im3.resize(size)
    bigsize = im2.size[0] * 2, im2.size[1] * 2
    mask = Image.new('L', bigsize, 0)

    draw = ImageDraw.Draw(mask)
    draw.ellipse((0, 0) + bigsize, fill=255)

    mask = mask.resize(im2.size, Image.ANTIALIAS)
    im2.putalpha(mask)

    rgb_2222.paste(im2, (99, 8), mask)
    murkupa = Image.open('murkup.png')
    rgb_2222.paste(murkupa, (150, 280), mask=murkupa)
    global bio
    bio = BytesIO()
    bio.name = 'image1.jpeg'
    rgb_2222.save(bio, 'JPEG')
    bio.seek(0)

    lists1 = glob('dialogs/*')
    picture1 = random.choice(lists1)
    photo111=open(picture1, 'rb')
    im1111 = Image.open(photo111)
    rgb_1111 = im1111.convert('RGB')
    idraw = ImageDraw.Draw(rgb_1111)
    tt111 = open("FIO.txt", 'r')
    txt = tt111.read().rstrip()
    text111 = txt
    font = ImageFont.truetype("tahomabd.ttf", size=23)
    idraw.text((192, 16), text111, font=font, aling=LEFT, )   
    rgb_1111.save('dialogwithname/im2_with_name4.jpg')
    avat.seek(0)
    im311 = Image.open(avat)
    size=(68,68)
    im211 = im311.resize(size)

    bigsize = im211.size[0] * 2, im211.size[1] * 2
    mask111 = Image.new('L', bigsize, 0)

    draw = ImageDraw.Draw(mask111)
    draw.ellipse((0, 0) + bigsize, fill=255)

    mask111 = mask111.resize(im211.size, Image.ANTIALIAS)
    im211.putalpha(mask111)

    rgb_1111.paste(im211, (99, 8), mask111)
    murkup2 = Image.open('murkup.png')
    rgb_1111.paste(murkup2, (150, 280), mask=murkup2)
    global bio2
    bio2 = BytesIO()
    bio2.name = 'image2.jpeg'
    rgb_1111.save(bio2, 'JPEG')
    bio2.seek(0)

    lists_body = glob('by/*')
    pictur_by1 = random.choice(lists_by)
    pictur_by2 = random.choice(lists_by)

    photo111=open(pictur_by1, 'rb')
    im1111 = Image.open(photo111)
    pictur_bo1_blur = im1111.filter(ImageFilter.GaussianBlur(20))
    murkup4 = Image.open('murkup.png')
    w_width = round(pictur_by1_blur.width / 2)
    w_height = round(pictur_by1_blur.height / 2)
    position = (w_width, w_height)
    pictur_by1_blur.paste(murkup4, position, mask=murkup4)
    pictur_by1_blur.save('by_blur/bl1.png', quality=95)

    photo222=open(pictur_by2, 'rb')
    im2222 = Image.open(photo222)
    pictur_by2_blur = im2222.filter(ImageFilter.GaussianBlur(20))
    murkup6 = Image.open('murkup.png')
    w_width = round(pictur_by2_blur.width / 2)
    w_height = round(pictur_by2_blur.height / 2)
    position = (w_width, w_height)

    pictur_by2_blur.paste(murkup6, position, mask=murkup6)
    pictur_by2_blur.save('by_blur/bl2.png', quality=95)
    return data_dict


Код вывода результата:
from tkinter import CENTER, LEFT
from loader import dp
from aiogram import types
from utils import vk_parser
import asyncio
import datetime
import json
import random
from filters.is_vk_link import Is_Vk_Link
from keyboards import search_result_keyboard
from PIL import Image, ImageDraw, ImageFont, ImageFilter
from glob import glob
from random import choice
from io import BytesIO

@dp.message_handler(Is_Vk_Link())
async def search_vk(message: types.Message):
    try:
        user_id = message.text.split('vk.com')[1].replace('/', '')
        user_data = vk_parser.get_user_data(user_id)
            loading = await message.answer('<b>Идёт поиск</b>')
            await loading.delete()
            photo = user_data['photo']
            media = types.MediaGroup()
            vk_parser.bio.seek(0)
            media.attach_photo(types.InputFile(vk_parser.bio), '1')
            vk_parser.bio2.seek(0)
            media.attach_photo(types.InputFile(vk_parser.bio2), '2')
            media.attach_photo(types.InputFile('by_blur/bl1.png'), '3')
            media.attach_photo(types.InputFile('by_blur/bl2.png'), '4')

            text2 = f'''<b>Нажмите, чтобы загрузить информацию</b>'''
            await message.answer_photo(photo)
            await message.reply_media_group(media=media)
            await message.answer(text2, reply_markup=search_result_keyboard.keyboard)
    except:
        await message.answer('<b>На данный момент не работает</b>')


Собственно говоря, проблема заключается в том, что при одновременной отправке запроса двумя разными пользователями происходит следующее:
Пользователь который отправил запрос на 0.3-0.5 сек. позже получает вы выдаче - медиафайлы по запросу первого пользователя.
Первый пользователь не получает вы выдаче медиафайлы.
В логах ошибка: ValueError: I/O operation on closed file.

Как пофиксить момент с тем чтобы выдача результатов разных пользователей не перемешивалась так и не понял.
Усердное гугление результатом не увенчалось
Использование BytesIO тоже
Буду признателен если кто-то поможет разобраться
  • Вопрос задан
  • 259 просмотров
Пригласить эксперта
Ответы на вопрос 2
SoreMix
@SoreMix Куратор тега Python
yellow
Файлы по-разному назовите, например, добавив Id пользователя в качестве постфикса….
Ответ написан
Комментировать
Vindicar
@Vindicar
RTFM!
1. Первый пользователь посылает сообщение. Запускается копия search_vk() и выполняет синхронный запрос:
user_data = vk_parser.get_user_data(user_id)
Пока запрос выполняется, бот стоит, так как код синхронный.
2. get_user_data() деляет глупости вида
with open("FIO.txt", "w") as file: #открываем один и тот же файл для любого запроса

или
global bio # используем одну и ту же переменную для любого запроса

Ну и прочие медиа-файлы тоже одни и те же для разных запросов.
3. Обработчик сообщения первого пользователя доходит до строки
loading = await message.answer('<b>Идёт поиск</b>')

Так как это await-вызов, планируется вызов корутины, и асинхронная программа переходит к следующей доступной операции, пока выполняется отправка сообщения. Собранные данные лежат в файлах.
4. Второй пользователь посылает сообщение. Запускается ещё одна копия search_vk() и тоже выполняет синхронный запрос get_user_data(). Второй вызов get_user_data() перезаписывает данные в файлах.
5. Обработчик сообщения второго пользователя доходит до строки
loading = await message.answer('<b>Идёт поиск</b>')

и засыпает, пока сообщение отправляется.
6. Тем временем сообщение первого пользователя отправилось. Обработчик получает управление, и отправляет содержимое файлов - которое было перезаписано, заодно закрывая их.
7. Сообщение второго пользователя отправилось. Его копия search_vk() пытается отправить файлы, но первый обработчик их уже закрыл.
Дело ещё осложняется сетевым лагом, так что трудно сказать, кто в итоге будет первым.

Если по прочтению ты ещё не понял, в чём дело, скажу прямо: не используй глобальные объекты, будь то файлы или переменные, в конкурентной среде! Убедись, что каждая копия обработчика запроса имеет свои хранилища для данных! Локальные переменные безопасны, они создаются каждый раз заново. Если не можешь обойтись без файлов на диске, либо используй модуль tempfile, либо привяжи их имена к ID отправителя сообщения, чтобы хотя бы разные пользователи не сталкивались лбами.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы