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

使用resumable.js+Django大文件上传:分片及文件消失问题排查

排查django-resumable大文件上传问题的实用步骤

我之前也遇到过类似的问题,下面一步步帮你定位和解决这些问题:

一、先确认分片的实际存储位置

你当前的视图把chunks_dir指向了MEDIA_ROOT,但django-resumable并不会直接把分片存在MEDIA_ROOT根目录下——它会自动生成一个基于文件唯一标识(比如hash值)的子目录来存储分片。

你可以做这些验证:

  • 先确认MEDIA_ROOT的绝对路径是否正确:在Django shell里执行from django.conf import settings; print(settings.MEDIA_ROOT),确保路径和你预期的一致。
  • 在视图里添加日志,打印实际的分片存储路径:
    import logging
    logger = logging.getLogger(__name__)
    
    class UserFileUploadView(ResumableUploadView):
        @property
        def chunks_dir(self):
            chunks_dir = getattr(settings, 'MEDIA_ROOT', None)
            if not chunks_dir:
                raise ImproperlyConfigured('You must set settings.MEDIA_ROOT')
            logger.info(f"Chunk storage directory: {chunks_dir}")
            return chunks_dir
        
        def get_chunk_path(self, request):
            path = super().get_chunk_path(request)
            logger.info(f"Current chunk will be saved to: {path}")
            return path
    
    查看日志里的路径,直接去对应的目录找分片文件。
  • 检查目录权限:确保运行Django的服务器进程(比如gunicorn、uwsgi对应的用户)有读写MEDIA_ROOT的权限,Linux下可以用chown -R www-data:www-data /your/media/root调整权限。

二、验证分片是否真的上传成功

有时候前端显示上传成功,但后端实际没收到,或者返回了错误状态码:

  1. 后端日志监控:重写post方法记录每个分片的上传请求:
    def post(self, request, *args, **kwargs):
        logger.info(f"Received chunk #{request.POST.get('resumableChunkNumber')} for file {request.POST.get('resumableFileName')}")
        response = super().post(request, *args, **kwargs)
        logger.info(f"Chunk upload response status: {response.status_code}")
        return response
    
    如果日志里没收到请求,说明前端和后端的请求路径不匹配;如果状态码不是200,说明后端处理出错。
  2. 前端事件监听:在resumable.js里添加事件监听,确认前端的上传状态:
    resumable.on('chunkSuccess', function(file, message) {
        console.log(`Chunk ${file.chunks.length} uploaded successfully`);
    });
    resumable.on('error', function(message, file) {
        console.error(`Upload error: ${message}`, file);
    });
    
    对比前后端日志,确认是否真的完成了分片上传。

三、解决ConnectionAbortedError和重新上传忽略分片的问题

1. 处理ConnectionAbortedError

这个错误是前端取消上传时,后端连接被中断导致的,直接捕获即可避免抛出500错误:

def post(self, request, *args, **kwargs):
    try:
        return super().post(request, *args, **kwargs)
    except ConnectionAbortedError:
        logger.warning("Connection aborted during chunk upload - ignoring")
        return HttpResponse(status=204)

2. 修复重新上传忽略分片的问题

这个问题通常是前端没有触发分片检查,或者后端没有正确返回已上传的分片:

  • 前端:在重新上传前先调用check()方法,让resumable.js检查已上传的分片:
    // 重新上传前执行
    resumable.check();
    resumable.upload();
    
  • 后端:重写get_uploaded_chunks方法,验证已上传分片的检测逻辑:
    def get_uploaded_chunks(self, request):
        uploaded_chunks = super().get_uploaded_chunks(request)
        logger.info(f"Found {len(uploaded_chunks)} uploaded chunks for file {request.POST.get('resumableFileName')}")
        return uploaded_chunks
    
    如果日志里显示已上传分片数量为0,说明分片存储路径和检测路径不匹配,回到第一步确认路径。

四、上传完成后无需刷新即可重新上传

这是因为resumable.js的文件队列没有清空,上传完成后需要手动移除文件或重置实例:

resumable.on('fileSuccess', function(file, message) {
    // 处理上传成功的业务逻辑
    resumable.removeFile(file); // 从队列中移除当前文件
    // 如果需要允许再次选择同一个文件,可以重置实例
    // resumable = new Resumable({/* 你的配置 */});
});

另外,django-resumable默认不会自动删除分片文件,你可以在合并完成后手动清理:

import shutil
from os.path import dirname

def post(self, request, *args, **kwargs):
    response = super().post(request, *args, **kwargs)
    // 判断是否是最后一个分片,合并完成后清理分片目录
    if request.POST.get('resumableChunkNumber') == request.POST.get('resumableTotalChunks'):
        chunk_path = self.get_chunk_path(request)
        chunk_dir = dirname(chunk_path)
        shutil.rmtree(chunk_dir)
        logger.info(f"Cleaned up chunk directory: {chunk_dir}")
    return response

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 07:14:10