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

Django如何实现异步文件上传?Celery使用及报错问题咨询

Django大文件异步上传落地方案

核心问题澄清

大文件上传过程中客户端无法操作其他功能,本质是两个原因导致的:一是前端用同步表单提交,上传时会锁死页面交互;二是后端把上传、文件处理全放在同步请求逻辑里,长连接占住导致响应慢。

Celery的适用边界

Celery不适合直接承接文件上传的传输过程,仅适合处理文件上传完成后的后端耗时逻辑(比如转码、病毒扫描、元数据解析、缩略图生成)。
你之前遇到的Object of type module is not JSON serializable报错,根因是Celery任务参数只支持可JSON序列化的基础类型(字符串、数字、普通字典/列表),你直接传入Django表单实例、request对象、内存文件对象这类绑定了请求上下文、WSGI连接引用的复杂实例,跨进程传参时序列化必然失败。就算绕开序列化限制,请求结束后连接释放、内存回收,worker拿到的也是无效对象,逻辑依然跑不通。

完整可运行实现方案

整套方案拆成三层,完全可以实现上传时用户正常操作网站其他功能,无阻塞:

  • 前端交互层(解决客户端阻塞的核心)
    放弃默认表单同步submit提交,用异步请求+分片逻辑实现上传:
    1. 选文件后给每个文件生成唯一ID,按固定大小(建议2MB-10MB/片)用Blob.slice()方法切片,不要一次性把整个大文件读入内存,避免浏览器崩溃
    2. 用XMLHttpRequest或fetch逐片发送分片请求,所有请求走独立异步线程,不会阻塞页面其他JS逻辑执行,用户可以正常点击、跳转网站其他功能
    3. 所有分片发送完成后,给后端发文件合并请求,轮询或用WebSocket接收后端处理完成的通知
  • 后端传输层
    Django端直接写两个普通接口承接分片上传即可,不需要把传输逻辑扔给Celery:
    1. 分片接收接口:接收前端传的文件唯一ID、分片序号、分片文件内容,存到服务器临时目录,直接返回成功响应
      参考代码:
    import os
    from django.http import JsonResponse
    from django.views.decorators.csrf import csrf_exempt
    
    TEMP_CHUNK_PATH = "/tmp/django_upload_chunks"
    os.makedirs(TEMP_CHUNK_PATH, exist_ok=True)
    
    @csrf_exempt
    def upload_chunk(request):
        if request.method != "POST":
            return JsonResponse({"code": 405, "msg": "方法不允许"})
        file_id = request.POST.get("file_id")
        chunk_idx = request.POST.get("chunk_index")
        chunk = request.FILES.get("chunk")
        save_dir = os.path.join(TEMP_CHUNK_PATH, file_id)
        os.makedirs(save_dir, exist_ok=True)
        with open(os.path.join(save_dir, f"{chunk_idx}.part"), "wb") as f:
            for block in chunk.chunks():
                f.write(block)
        return JsonResponse({"code": 0, "msg": "分片上传成功"})
    
    1. 分片合并接口:收到前端合并请求后,校验所有分片是否上传完整,按分片序号顺序拼接成完整文件,存到正式文件存储路径,清理临时分片目录
  • 后端异步处理层(Celery的正确使用位置)
    文件合并完成后,不要在请求逻辑里执行耗时操作,只把可序列化的基础参数(比如文件正式存储路径、文件ID、上传用户ID)传给Celery任务,接口立刻给前端返回上传成功响应,耗时逻辑交给后台worker执行即可。
    参考代码:
    from celery import shared_task
    
    @shared_task
    def post_upload_process(file_path: str, file_id: str, upload_user_id: int):
        # 此处写耗时逻辑:比如视频转码、内容安全扫描、生成缩略图、写入数据库关联关系
        # 禁止传入request、表单实例、内存文件对象这类非序列化参数
        print(f"开始处理文件:{file_id},存储路径:{file_path}")
        return True
    
    合并文件完成后,直接调用post_upload_process.delay(final_save_path, file_id, request.user.id)即可触发异步任务,不会出现序列化报错。

注意:不要为了图省事把整个文件内容读成字符串传给Celery,大文件内容传参会占满Celery的消息队列带宽,严重时会导致Broker崩溃,只传文件存储路径让worker自己读文件是最优方案。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 02:57:33