Paramiko exec_command无法等待任务完成的Django Docker应用问题
解决Django异步任务未完成时视图提前读取文件的问题
问题本质
调用my_task.delay()后,异步任务是在后台独立执行的,但视图会立刻去读取还没生成/未完成的zip文件,自然会出问题。
可行解决方案
方案1:用Celery任务状态做前端轮询(推荐)
让前端先触发任务,再轮询任务状态,等任务完成后再请求下载文件,完全适配异步场景。
调整tasks.py
确保任务能正确反馈执行状态,并且等待SSH命令执行完毕再返回:
@shared_task(bind=True) def my_task(self): # 去掉原命令里的until循环——zip/mv成功会直接退出,失败返回非0状态,不需要循环重试 command2 = """sudo cp /.../ibt1.msh /.../ibt1.msh && \ sudo zip -r result.zip ./output/ && \ sudo mv result.zip /europlexusData/result.zip && \ sudo rm -rf ./output """ host2 = "你的目标容器地址" port = 22 # 替换为实际SSH端口 username = "SSH用户名" password = "SSH密码" ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) try: ssh.connect(host2, port, username, password) # 执行命令并等待执行结束 stdin, stdout, stderr = ssh.exec_command(command2, get_pty=True) exit_status = stdout.channel.recv_exit_status() if exit_status != 0: error_msg = stderr.read().decode() self.update_state(state='FAILURE', meta={'error': error_msg}) raise Exception(f"命令执行失败: {error_msg}") return {'status': 'success', 'file_path': '/europlexusData/result.zip'} except Exception as e: self.update_state(state='FAILURE', meta={'error': str(e)}) raise finally: ssh.close()
新增视图逻辑
拆分出三个视图:触发任务、检查任务状态、下载文件:
from django.http import JsonResponse, FileResponse from .tasks import my_task import os def trigger_task(request): task = my_task.delay() return JsonResponse({'task_id': task.id}) def check_task_status(request, task_id): task = my_task.AsyncResult(task_id) if task.state == 'SUCCESS': return JsonResponse({ 'state': task.state, 'ready': True }) elif task.state == 'FAILURE': return JsonResponse({ 'state': task.state, 'error': task.result.get('error'), 'ready': False }) else: return JsonResponse({'state': task.state, 'ready': False}) def download_result(request): file_path = '/europlexusData/result.zip' if os.path.exists(file_path): return FileResponse(open(file_path, 'rb'), filename='result.zip') else: return JsonResponse({'error': '文件尚未生成'}, status=404)
前端配合逻辑
先调用trigger_task拿到task_id,然后每隔几秒请求check_task_status,直到返回ready: True,再调用download_result下载文件。
方案2:视图同步等待任务完成(不推荐)
如果非要在同一个请求里完成,可以让视图等待任务结束,但会阻塞Django进程,不适合高并发场景:
from django.http import FileResponse from .tasks import my_task import os def my_view(request): task = my_task.delay() # 设置超时时间,比如5分钟 result = task.get(timeout=300) file_path = '/europlexusData/result.zip' if os.path.exists(file_path): return FileResponse(open(file_path, 'rb'), filename='result.zip') else: return JsonResponse({'error': '任务执行失败,文件未生成'}, status=500)
方案3:WebSocket主动通知(进阶)
用Django Channels实现WebSocket,任务完成后主动推送给前端,让用户立刻知道可以下载,体验更好。
额外优化点
- 尽量避免在SSH命令里用
sudo,可以给SSH用户配置对应目录的权限,或者用有权限的用户连接。 - 给生成的zip文件加唯一标识(比如任务ID),防止多任务同时执行时文件被覆盖。
- 给任务添加日志记录,方便排查执行异常。
内容的提问来源于stack exchange,提问作者CLU
相关产品推荐
相关产品推荐

