Django/Python 如何实现无需刷新的实时下载进度计数显示
核心问题说明
你现在的代码从根上就不满足实时更新的逻辑,有两个绕不开的问题:
views.py里的while循环逻辑完全失效:你在循环体内算出进度值后立刻执行return render(...),第一次循环还没走完就直接把响应返回给浏览器、后端函数就退出了,根本不会执行第二次循环- 所有后端Web框架(包括Django)的基础运行逻辑都是「浏览器发1次请求,后端返回1次响应,连接随即断开」,你没法在一个后端的循环里,持续把不同的进度值推送到已经发给用户的页面上,这是Web的基础通信模式决定的,不是代码写得不对。
适合初学者的最简实现方案
不用碰复杂的实时通信技术,用「后端拆分接口+前端定时轮询」的方式就能实现无手动刷新的进度更新,改动量小、逻辑好懂:
第一步:重构后端视图逻辑
不要把触发下载、查询进度、返回页面的逻辑全塞在一个视图函数里,拆成两个独立接口:
- 触发下载的接口:用户点开始下载时调用,负责和qbittorrent客户端通信启动下载,直接返回进度展示页面,不在这个接口里循环等进度跑完
- 进度查询接口:专门供前端定时调用,每次被访问时实时计算当前下载进度,返回JSON格式的数值,不用返回完整的HTML页面
对应views.py的修改示例:
# 入门阶段可以先用全局变量临时存任务标识,快速跑通逻辑 qb_client = Client("http://www.on-demandlogistics.com:8080/") qb_client.login("admin", "adminadmin") # 存当前下载任务的torrent hash current_task_hash = "cca0bf46ce95c802e6a8f0e1f353978d12da6997" def start_download_view(request): qb_client.download_from_link(torrentLink) # 直接返回进度页面,不做循环等待 return render(request, "movies/dbsearch.html", { "movie_name": "Downloading " + movie_name[0].title() + " now!" }) def query_progress_view(request): # 每次被调用时实时查最新进度 torrent_info = qb_client.get_torrent(current_task_hash) progress = int( float(torrent_info['total_downloaded']) / float(torrent_info['total_size']) * 100 ) # 判断是否下载完成 is_finished = progress >= 100 # 返回JSON格式的进度数据 return JsonResponse({ "progress": progress, "is_finished": is_finished })
记得在项目的urls.py里注册这两个新接口的路由:
from django.urls import path from . import views urlpatterns = [ path('start-download/', views.start_download_view), path('api/query-progress/', views.query_progress_view), ]
第二步:修改前端页面,加定时自动更新逻辑
在原来的页面基础上,用最基础的原生JS写定时轮询逻辑,不需要引入任何前端框架:
<!DOCTYPE html> <html> <head> <title>dbSearch</title> </head> <body> <h1>{{movie_name}}</h1> <!-- 给进度元素加id,方便JS定位更新 --> <h2 id="progress-text">0%</h2> <!-- 简单的可视化进度条 --> <div style="width: 80%; height: 20px; background: #f0f0f0; margin: 10px 0;"> <div id="progress-bar" style="height: 100%; width: 0%; background: #2196f3;"></div> </div> <script> // 每1000毫秒(1秒)请求一次后端的进度接口 const refreshTimer = setInterval(async () => { // 调用后端的进度查询接口 const response = await fetch('/api/query-progress/'); const progressData = await response.json(); // 把拿到的进度值更新到页面上 document.getElementById('progress-text').innerText = progressData.progress + '%'; document.getElementById('progress-bar').style.width = progressData.progress + '%'; // 进度到100%时停止定时查询,改成显示完成提示 if (progressData.is_finished) { clearInterval(refreshTimer); document.getElementById('progress-text').innerText = 'Download Complete!'; } }, 1000) </script> </body> </html>
入门阶段需要注意的细节
- 示例里用全局变量存任务信息只是为了快速跑通逻辑,如果后续要支持多用户同时使用,需要给每个用户的下载任务绑定唯一标识,存在用户会话或者数据库里,否则多用户访问时进度会互相串
- 这种定时轮询的方式逻辑简单、好调试,对于下载进度这种不需要毫秒级实时的场景完全够用,不用一开始就强行上WebSocket、SSE等复杂的实时通信方案,等熟悉了Web通信逻辑再优化也不迟
- 你原来的代码里两次调用
qb.get_torrent()查同一个任务的信息,可以像示例里那样把查询结果存在临时变量里复用,减少重复的API请求,提升效率
内容的提问来源于stack exchange,提问作者littlejiver
相关产品推荐
相关产品推荐

