Django使用Redis-Queue执行后台任务后如何将结果回传前端展示下载按钮
实现方案
整体逻辑
- 任务提交入队后将RQ生成的
job_id返回给前端页面 - 前端携带
job_id定时轮询后端的任务状态查询接口 - 后端根据
job_id从RQ中查询任务状态,任务完成则返回生成的文件名,失败返回错误信息 - 前端根据返回结果动态渲染下载按钮或错误提示
1. 调整视图文件views.py
新增任务状态查询接口,同时修改原提交逻辑传递job_id到模板:
from django.shortcuts import render from django.contrib import messages from django.views.decorators.csrf import csrf_exempt from django.http import HttpResponse, JsonResponse from rq.job import Job from redis import Redis from .forms import ScriptParamForm from .queue_job import my_job # 和你启动RQ worker的Redis配置保持一致 redis_conn = Redis(host='localhost', port=6379, db=0) @csrf_exempt def home(request): context = {} if request.method == 'POST': if request.POST.get("btn_submit"): req_dict = dict(request.POST) form = ScriptParamForm(request.POST) context['form'] = form if form.is_valid(): req_dict = dict(request.POST) # 确保my_job是RQ入队操作,返回Job实例 job = my_job(outdir=tmp_dir, **req_dict) context['job_id'] = job.id return render(request, 'webapp/home.html', context) elif request.POST.get("btn_download"): serve_file = request.POST.get("btn_download") with open(serve_file, 'rb') as fp: response = HttpResponse(fp, headers={ 'Content-Type': 'application/zip', 'Content-Disposition': f'attachment; filename="{serve_file}"' }) return response else: form = ScriptParamForm() context['form'] = form return render(request, 'webapp/home.html', context) # 新增任务状态查询接口 @csrf_exempt def check_job_status(request): if request.method == 'POST': job_id = request.POST.get('job_id') try: job = Job.fetch(job_id, connection=redis_conn) except: return JsonResponse({'status': 'invalid', 'msg': '任务ID不存在'}) if job.is_finished: # job.result为my_job执行完成后return的文件路径 return JsonResponse({'status': 'finished', 'serve_file': job.result}) elif job.is_failed: return JsonResponse({'status': 'failed', 'msg': '任务执行失败,请重试'}) else: return JsonResponse({'status': 'running'}) return JsonResponse({'status': 'error'})
同步添加路由到urls.py:
from django.urls import path from . import views urlpatterns = [ path('', views.home, name='home'), path('check-job-status/', views.check_job_status, name='check_job_status'), ]
2. 修改模板文件home.html
新增job_id存储字段,调整下载按钮默认隐藏:
{% extends 'base.html' %} {% block content %} <form method="POST" autocomplete="on"> {% csrf_token %} <!-- 表单字段区域 --> ... <!-- 新增隐藏域存储job_id --> <input type="hidden" id="job-id" value="{{ job_id|default:'' }}"> <!-- 按钮区域 --> <div> <button type="submit" name="btn_submit" value="submit" class="btn btn-primary" id="btn-submit">Submit</button> <!-- 下载按钮默认隐藏,任务完成后JS控制显示 --> <button type="submit" name="btn_download" id="download-btn" value="" class="btn btn-primary d-none">Download</button> <span class="not-visible" id="calc-msg"> This may take a few, relax! :) </span> <span id="error-msg" class="text-danger d-none"></span> </div> </form> {% endblock content %}
3. 修改JS文件main.js
添加轮询逻辑,动态更新页面状态:
var x = document.getElementById("btn-submit") var jobId = document.getElementById("job-id").value var pollTimer = null x.onclick = function () { $("#calc-msg").removeClass("not-visible"); setInterval(function(){blink()}, 1000); // 提交任务后启动轮询,每3秒查询一次状态 if(jobId) { pollTimer = setInterval(checkJobStatus, 3000) } }; function blink() { $("#calc-msg").fadeTo(100, 0.1).fadeTo(200, 1.0); } function checkJobStatus() { $.post('/check-job-status/', { 'job_id': jobId, 'csrfmiddlewaretoken': $('input[name="csrfmiddlewaretoken"]').val() }, function(res) { if(res.status === 'finished') { // 任务完成,停止轮询,显示下载按钮 clearInterval(pollTimer) $("#calc-msg").addClass("not-visible"); $("#download-btn").val(res.serve_file).removeClass("d-none") } else if(res.status === 'failed' || res.status === 'invalid') { // 任务失败,停止轮询,显示错误信息 clearInterval(pollTimer) $("#calc-msg").addClass("not-visible"); $("#error-msg").text(res.msg).removeClass("d-none") } }) }
注意:你的
my_job任务函数执行完成后需要返回生成的压缩文件的完整路径,才能在job.result中拿到对应文件名用于下载。
内容的提问来源于stack exchange,提问作者Johngoldenboy
相关产品推荐
相关产品推荐

