上传文件的零星腐败

时间:2015-09-16 15:16:26

标签: python django

下面的代码采用HTTP请求(Ajax)中发送的文件并将其保存到服务器。代码是由其他人编写的,但我最近必须修改它以向文件添加唯一标识符,以便不会覆盖具有相同名称的现有文件。基本上,我添加了以下几行:

#uid is a GUID
if os.path.isfile(destination):
            destination = os.path.splitext(destination)[0] + str(uid) + os.path.splitext(destination)[1]
            name  = os.path.splitext(name)[0] + str(uid) + os.path.splitext(name)[1]

我现在看到的问题是有时我将UID添加到文件名的文件以保证唯一性,最终被破坏。它并不总是发生 - 大多数情况下文件保存正确但在过去7天中至少4个案例中的11个案例中,文件已损坏且只发生在添加了UID的文件中将名称保存到文件系统之前的名称。此代码是否有任何错误可能导致文件损坏?

以下是使用方法的完整上下文:

if form.is_valid():
        id = request.REQUEST.get('id','')
        file = request.FILES['file']
        chunk = request.REQUEST.get('chunk','0')
        chunks = request.REQUEST.get('chunks','0')
        name = request.REQUEST.get('name','')
        destination = settings.MEDIA_ROOT+'/files/%s' % name
        # If the code goes into the below IF, the file MAY get corrupted.
        if os.path.isfile(destination):
            destination = os.path.splitext(destination)[0] + str(uid) + os.path.splitext(destination)[1]
            name  = os.path.splitext(name)[0] + str(uid) + os.path.splitext(name)[1]
        with open(destination, ('wb' if chunk == '0' else 'ab')) as f:  
            for content in file.chunks():  
                f.write(content)  
        if int(chunk) + 1 >= int(chunks):
            if not Attachment.objects.filter(uuid=uid,user=username,name=name):
                form.save(name,username,uid,id)

    response = HttpResponse(json.dumps({"jsonrpc" : "2.0", "result" : None, "id" : "id"}), mimetype='text/plain; charset=UTF-8')  
    return response

1 个答案:

答案 0 :(得分:1)

问题似乎是文件uid的生命周期。对于单个文件上载,这不是问题,但在使用代码的分块上传功能时会出现问题。

因为每个请求生成uid并且每个文件块都在单独的请求中上传,所以每个块都会收到不同的uid。这反过来导致块1 文件1 块2 转移到文件2 ,从而导致损坏。< / p>

一种解决方法是根据会话密钥设置uid,可通过request.session.session_key获取。由于会话密钥的加密属性,它也应该“合理地唯一”用于此目的。

但请注意,如果文件路径暴露给Web,或者即使/media/可以在目录中列出,也存在潜在的安全风险,因为您正在将会话密钥公开给Web(会话) key是唯一可以保护对活动会话的访问的方式。)

另一种更安全的方法是通过会话变量为每个会话分配唯一​​的UUID。这可能最好在middleware中完成:

class SessionUUIDMiddleware(object):
    def process_request(request):
        session_uuid = request.session.get('uuid', None)
        if not session_uuid:
            session_uuid = uuid.uuid1()
            request.session['uuid'] = session_uuid

这会断开会话密钥中的唯一ID。