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

在discord.py、flask-socketio与Flask环境下,如何通过监听套接字向服务器发送文件?

在discord.py、flask-socketio与Flask环境下,如何通过监听套接字向服务器发送文件?

看起来你已经搭建了基于Flask-SocketIO的文件上传服务,尝试将文件转发到Discord,但大概率遇到了进度条不实时更新、请求阻塞超时或者Discord消息发送失败的问题?我来帮你分析代码里的关键问题并给出可运行的优化方案:

你的代码中存在的核心问题

  1. Discord操作阻塞主事件循环:discord.py的get_channel和channel.send都是同步/异步阻塞操作,会卡住Flask-SocketIO的事件循环,导致进度通知无法实时推送,甚至整个请求超时。
  2. 频道ID类型错误:bot.get_channel("channel id")需要传入整数类型的频道ID,字符串ID会直接导致获取频道失败。
  3. 同步IO阻塞响应:文件写入、数据库操作如果遇到大文件,会阻塞SocketIO的响应线程,让客户端感觉服务“卡住”了。
  4. 缺乏资源清理:文件发送到Discord后没有删除本地副本,会持续占用服务器磁盘空间。

优化后的完整代码

import os
import random
import sqlite3
from threading import Thread
import traceback
from flask_socketio import emit
import discord
from discord.ext import commands

# 假设你的bot和socketio实例已正确初始化
# bot = commands.Bot(command_prefix="!", intents=discord.Intents.all())
# socketio = SocketIO(app, cors_allowed_origins="*")

@socketio.on('upload')
def handle_upload(data):
    def upload_worker():
        try:
            random_id = str(random.randint(1000000000, 9999999999))
            username = "skyrocketer"
            file = data['file']
            filename = data['filename']
            # 用os.path.join避免路径拼接错误
            file_path = os.path.join(os.getcwd(), 'static', f'{random_id}-{filename}')

            # 推送进度:初始化完成
            emit('progress', {'percent': '25'}, namespace='/')

            # 数据库写入操作
            with sqlite3.connect('ddrv.db') as conn:
                cursor = conn.cursor()
                cursor.execute(
                    "INSERT INTO files (user, file_id, file_name) VALUES (?, ?, ?)", 
                    (username, random_id, filename)
                )
                conn.commit()

            emit('progress', {'percent': '50'}, namespace='/')
            
            # 分块写入文件(大文件友好)
            chunk_size = 4096
            total_bytes = len(file)
            written_bytes = 0
            with open(file_path, 'wb') as f:
                for i in range(0, total_bytes, chunk_size):
                    chunk = file[i:i+chunk_size]
                    f.write(chunk)
                    written_bytes += len(chunk)
                    # 可选:计算真实进度替代固定值
                    # real_percent = min(75, 50 + (written_bytes / total_bytes) * 25)
                    # emit('progress', {'percent': str(int(real_percent))}, namespace='/')

            emit('progress', {'percent': '75'}, namespace='/')
            
            # 修正:频道ID必须是整数
            channel = bot.get_channel(123456789012345678)  # 替换成你的真实频道ID(整数)
            if not channel:
                raise ValueError("无法找到指定的Discord频道,请检查ID是否正确或bot是否有权限访问")
            
            # 适配异步discord.py的send方法
            with open(file_path, 'rb') as f:
                discord_file = discord.File(f)
                # 如果是异步bot,必须通过事件循环执行异步方法
                if hasattr(bot, 'loop'):
                    bot.loop.run_until_complete(channel.send(file=discord_file))
                else:
                    channel.send(file=discord_file)

            emit('progress', {'percent': '100'}, namespace='/')
            
            # 可选:发送完成后清理本地文件(如果不需要保留的话)
            if os.path.exists(file_path):
                os.remove(file_path)

        except Exception as e:
            # 打印完整异常栈,方便调试
            print(f"Error handling upload: {e}")
            traceback.print_exc()
            emit('progress', {'percent': 'failed'}, namespace='/')

    # 用后台线程执行所有阻塞操作,避免卡住SocketIO主事件循环
    Thread(target=upload_worker, daemon=True).start()

关键优化点解释

  1. 后台线程隔离阻塞任务:把文件写入、数据库操作、Discord通信全部放到单独线程执行,确保Flask-SocketIO的主事件循环不被阻塞,进度通知能实时推送给客户端。
  2. 修正Discord频道ID类型:将字符串ID改为整数,这是discord.pyget_channel方法的强制要求,否则会返回None导致后续发送失败。
  3. 异步Discord操作适配:针对现代异步版discord.py,用bot.loop.run_until_complete()同步调用异步的channel.send方法,避免异步/同步模式冲突。
  4. 可选的真实进度计算:注释部分提供了根据文件写入字节数计算真实进度的逻辑,替代固定的百分比,让进度条更准确。
  5. 资源清理:文件发送完成后删除本地副本,避免磁盘空间被无用文件占用(如果需要长期存储可以注释掉这部分)。

额外建议

  • 使用异步数据库驱动:如果服务并发量高,建议用aiosqlite替代sqlite3,配合Flask-SocketIO的异步模式进一步提升性能。
  • 增加权限校验:当前代码中username是固定值,建议从SocketIO的认证信息中获取真实用户,避免非法上传。
  • SocketIO异步模式:如果你的Flask-SocketIO用的是eventlet或gevent异步引擎,可以考虑使用@socketio.on('upload', async_mode='eventlet')装饰器,配合线程池处理阻塞操作。

如果还有其他问题(比如bot初始化配置、SocketIO跨域设置),可以把完整项目代码贴出来,我再帮你进一步排查!


备注:内容来源于stack exchange,提问作者Skyrocketer

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.14 13:28:10