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

Django实现任务进度消息实时显示的可行方案咨询

解决方法:基于Server-Sent Events(SSE)实现实时进度显示

1. 核心思路

原代码中calculate_features是同步执行的,会阻塞请求直到任务完成,导致前端无法实时获取进度。我们需要把耗时任务放到后台线程执行,同时通过SSE(服务器推送事件)向前端实时推送进度数据,前端监听SSE连接更新页面显示。

2. 后端改造

2.1 配置临时进度存储

在settings.py中配置缓存(生产环境建议用Redis,这里先用本地内存缓存示例):

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
        'LOCATION': 'seq-analysis-cache',
    }
}

2.2 重构上传视图,启动后台任务

修改upload_sequence视图,将calculate_features放到后台线程执行,避免阻塞主请求:

import os
import threading
from django.shortcuts import render, redirect
from django.http import JsonResponse
from django.core.cache import cache

def calculate_features(file_path, task_id):
    # 替换为你实际的总步骤数
    total_steps = 4
    completed_steps = 0

    try:
        # 步骤1:序列上传完成
        completed_steps += 1
        progress = int((completed_steps / total_steps) * 100)
        cache.set(task_id, {'message': f"Sequence uploaded successfully", 'progress': progress}, timeout=300)
        
        # 步骤2:序列预处理
        completed_steps += 1
        progress = int((completed_steps / total_steps) * 100)
        cache.set(task_id, {'message': "Preprocessing sequence", 'progress': progress}, timeout=300)
        
        # 步骤3:特征计算
        completed_steps += 1
        progress = int((completed_steps / total_steps) * 100)
        cache.set(task_id, {'message': "Calculating features", 'progress': progress}, timeout=300)
        
        # 步骤4:任务完成
        completed_steps += 1
        progress = 100
        cache.set(task_id, {'message': "Analysis completed", 'progress': progress, 'done': True}, timeout=300)
        
    except Exception as e:
        cache.set(task_id, {'message': f"Error: {str(e)}", 'progress': 0, 'error': True}, timeout=300)

def upload_sequence(request):
    if request.method == "POST":
        sequence = request.POST.get("sequence")
        file = request.FILES.get("file")
        # 生成唯一任务ID,用于跟踪进度
        task_id = f"task_{os.urandom(8).hex()}"
        
        try:
            script_dir = os.path.dirname(os.path.abspath(__file__))
            file_path = os.path.join(script_dir, "sequences.fasta")
            
            if sequence:
                with open(file_path, "w") as f:
                    f.write(sequence)
            elif file:
                with open(file_path, "wb") as f:
                    for chunk in file.chunks():
                        f.write(chunk)
            
            # 启动后台线程执行特征计算
            threading.Thread(target=calculate_features, args=(file_path, task_id), daemon=True).start()
            
            # 返回任务ID给前端,用于查询进度
            return JsonResponse({"status": "started", "task_id": task_id})
            
        except Exception as e:
            print(f"Error preparing sequence: {e}")
            return JsonResponse({"status": "Error preparing sequence"}, status=500)
    
    return render(request, "upload.html")

2.3 新增SSE进度推送视图

创建一个持续向前端推送进度的视图:

from django.http import HttpResponse
import json
import time

def progress_stream(request, task_id):
    response = HttpResponse(content_type='text/event-stream')
    response['Cache-Control'] = 'no-cache'
    response['Connection'] = 'keep-alive'
    
    while True:
        progress_data = cache.get(task_id)
        if not progress_data:
            # 任务未开始时的默认消息
            response.write(f"data: {{\"message\": \"Waiting for task to start...\", \"progress\": 0}}\n\n")
        else:
            # 将进度数据转为JSON字符串推送
            data_str = json.dumps(progress_data)
            response.write(f"data: {data_str}\n\n")
            
            # 任务完成或出错时终止连接
            if progress_data.get('done') or progress_data.get('error'):
                break
        
        # 刷新响应避免缓冲,每秒推送一次
        response.flush()
        time.sleep(1)
    
    return response

2.4 配置URL路由

在urls.py中添加进度视图的路由:

from django.urls import path
from . import views

urlpatterns = [
    path('upload/', views.upload_sequence, name='upload'),
    path('progress/<str:task_id>/', views.progress_stream, name='progress_stream'),
    path('results/', views.results, name='results'),  # 你的结果页面路由
]

3. 前端改造(upload.html)

添加进度显示区域和SSE监听逻辑:

<!DOCTYPE html>
<html>
<head>
    <title>Upload Sequence</title>
    <style>
        .progress-box {
            margin-top: 20px;
            display: none;
        }
        .progress-bar {
            width: 100%;
            height: 24px;
            border-radius: 12px;
            background: #f1f1f1;
            overflow: hidden;
        }
        .progress-fill {
            height: 100%;
            background: #2ecc71;
            width: 0%;
            transition: width 0.3s ease;
        }
        .progress-message {
            margin-top: 8px;
            font-size: 14px;
            color: #333;
        }
    </style>
</head>
<body>
    <h1>Sequence Upload & Analysis</h1>
    <form id="uploadForm">
        <textarea name="sequence" rows="6" placeholder="Paste your sequence here..."></textarea>
        <br><br>
        <label>Or upload FASTA file:</label>
        <input type="file" name="file" accept=".fasta,.fa">
        <br><br>
        <button type="submit">Start Analysis</button>
    </form>

    <div class="progress-box" id="progressBox">
        <div class="progress-bar">
            <div class="progress-fill" id="progressFill"></div>
        </div>
        <div class="progress-message" id="progressMsg"></div>
    </div>

    <script>
        document.getElementById('uploadForm').addEventListener('submit', async (e) => {
            e.preventDefault();
            const formData = new FormData(e.target);
            
            // 发送上传请求
            const resp = await fetch('/upload/', {
                method: 'POST',
                body: formData
            });
            
            if (resp.ok) {
                const result = await resp.json();
                const taskId = result.task_id;
                
                // 显示进度区域
                document.getElementById('progressBox').style.display = 'block';
                
                // 建立SSE连接监听进度
                const eventSource = new EventSource(`/progress/${taskId}/`);
                
                eventSource.onmessage = (event) => {
                    const data = JSON.parse(event.data);
                    // 更新进度条和消息
                    document.getElementById('progressFill').style.width = `${data.progress}%`;
                    document.getElementById('progressMsg').textContent = data.message;
                    
                    // 任务完成后关闭连接并跳转结果页
                    if (data.done) {
                        eventSource.close();
                        setTimeout(() => window.location.href = '/results/', 1500);
                    }
                    
                    // 出错时提示并关闭连接
                    if (data.error) {
                        eventSource.close();
                    }
                };
                
                eventSource.onerror = () => {
                    eventSource.close();
                    document.getElementById('progressMsg').textContent = 'Connection lost, please refresh';
                };
            } else {
                const error = await resp.json();
                alert(error.status);
            }
        });
    </script>
</body>
</html>

4. 生产环境优化建议

  • 用Redis替代本地内存缓存,避免多进程/多服务器环境下进度数据无法共享。
  • 若任务执行时间超过30秒,建议改用Celery等专业任务队列框架,配合Redis存储进度,比后台线程更稳定。
  • 前端可添加SSE断开自动重连逻辑,提升稳定性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 14:07:03