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

Django中用FileField与HTML5视频标签播放视频时遇ConnectionResetError

解决Django日志中持续出现的ConnectionResetError(视频播放时用户离开页面)

首先,你推测的原因完全正确:这个ConnectionResetError: [Errno 54] Connection reset by peer是当用户在视频加载/播放过程中关闭页面、刷新或者导航到其他页面时,浏览器主动中断了和服务器的连接,导致Django服务器(尤其是开发环境的默认服务器)抛出这个错误。这本质上是正常的客户端行为,不会影响功能,但会产生烦人的日志噪音,我们可以通过以下方式解决:

1. 开发环境(Django自带runserver)

Django默认的开发服务器基于socketserver,对这类客户端主动断开连接的情况没有做异常处理,所以会把完整的栈追踪打出来。我们可以自定义一个请求处理器来忽略这个错误:

修改项目根目录下的manage.py,替换默认的请求处理器:

#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys
from django.core.servers.basehttp import run, WSGIRequestHandler


class QuietWSGIRequestHandler(WSGIRequestHandler):
    def handle(self):
        try:
            super().handle()
        except ConnectionResetError:
            # 忽略客户端连接重置的错误,避免日志刷屏
            pass


def main():
    """Run administrative tasks."""
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'your_project.settings')
    try:
        from django.core.management import execute_from_command_line
    except ImportError as exc:
        raise ImportError(
            "Couldn't import Django. Are you sure it's installed and "
            "available on your PYTHONPATH environment variable? Did you "
            "forget to activate a virtual environment?"
        ) from exc
    
    # 检测是否是runserver命令,替换处理器
    if len(sys.argv) >= 2 and sys.argv[1] == 'runserver':
        addr = sys.argv[2].split(':')[0] if len(sys.argv) > 2 else '127.0.0.1'
        port = int(sys.argv[3]) if len(sys.argv) > 3 else 8000
        run(addr=addr, port=port, handler_class=QuietWSGIRequestHandler)
    else:
        execute_from_command_line(sys.argv)


if __name__ == '__main__':
    main()

修改后,开发服务器遇到连接重置错误时就会静默处理,不会再输出冗长的错误栈。

2. 生产环境(Nginx+Gunicorn/uWSGI)

生产环境通常使用Gunicorn或uWSGI作为应用服务器,搭配Nginx反向代理。这类服务器默认会处理客户端中断连接的情况,但如果还是有日志输出,可以通过调整配置过滤该错误:

Gunicorn配置示例

创建gunicorn_config.py文件:

import logging
from gunicorn import glogging


class CustomGunicornLogger(glogging.Logger):
    def setup(self, cfg):
        super().setup(cfg)
        # 过滤包含ConnectionResetError的日志记录
        access_logger = logging.getLogger("gunicorn.access")
        for handler in access_logger.handlers:
            handler.addFilter(lambda record: "ConnectionResetError" not in record.getMessage())

启动Gunicorn时指定该配置:

gunicorn --config gunicorn_config.py your_project.wsgi:application

Nginx补充配置

Nginx会把客户端主动中断的请求标记为499 Client Closed Request,你可以在Nginx日志配置中忽略这个状态码的日志:

access_log /var/log/nginx/access.log combined exclude=499;

3. 优化视频访问路径(从根源避免请求经过Django)

如果你的视频存储在S3上,当前是通过Django代理请求(比如私有桶需要签名),可以直接让前端使用S3预签名URL访问视频,这样请求直接发送到S3,不会经过Django服务器,自然就不会产生这类错误。

在DetailView中生成预签名URL:

from django.views.generic.detail import DetailView
from storages.backends.s3boto3 import S3Boto3Storage
from .models import YourVideoModel


class VideoDetailView(DetailView):
    model = YourVideoModel
    template_name = 'video_detail.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        storage = S3Boto3Storage()
        # 生成有效期为1小时的预签名URL
        presigned_url = storage.url(self.object.video_sample.name, expire=3600)
        context['video_presigned_url'] = presigned_url
        return context

模板中使用预签名URL:

<video width='50%' controls>
    <source src="{{ video_presigned_url }}" type="video/mp4">
    Your browser does not support the video tag.
</video>

总结:这个错误本身不影响业务功能,核心是过滤日志噪音,或者让视频请求不经过Django服务器。根据你的部署环境选择对应的方案即可。

内容的提问来源于stack exchange,提问作者Shahar Gino

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 00:12:38