【问题标题】:Django silently discarding uploaded files with long pathsDjango静默丢弃上传的长路径文件
【发布时间】:2022-08-06 01:49:55
【问题描述】:

我遇到了一个问题,Django Rest Framework 似乎默默地丢弃了长路径上传的文件。

这是我的视图类和发布方法:

class UploadMediaViewSet(viewsets.ViewSet):
    parser_classes = [parser.MultiPartParser]

    # POST /api/upload/media/
    def create(self, request):
        LOG.info(f\"************** request.FILES = {request.FILES}\")

发送的表单数据如下:

------WebKitFormBoundaryBEDAIwXzG6Ik2xVY
Content-Disposition: form-data; name=\"transactionId\"

804d4146-0947-4d96-90b5-8ffbbc0b2135
------WebKitFormBoundaryBEDAIwXzG6Ik2xVY
Content-Disposition: form-data; name=\"oOJGp433ODZvBOZTCXNz1oO7ogG0j3BRRBo98jpx1iIlvMPeNoc8nBKvpoTjx9PsOl5ulGGWniur3TdbDSd9TpgsnWhhqurcQO3TnssSQNHWti7xm7nZGW6tFRtrjrvwoJm9Bds5AsMcNKxT7oBkzA35fA1fgo5jkiUAfHHiduMdGIYf3NJGk8LP54JAORfYEK05mdHdQ4zfpMKfDUNJLnc5tk3H/AndroidLandscape.mp4\"; filename=\"oOJGp433ODZvBOZTCXNz1oO7ogG0j3BRRBo98jpx1iIlvMPeNoc8nBKvpoTjx9PsOl5ulGGWniur3TdbDSd9TpgsnWhhqurcQO3TnssSQNHWti7xm7nZGW6tFRtrjrvwoJm9Bds5AsMcNKxT7oBkzA35fA1fgo5jkiUAfHHiduMdGIYf3NJGk8LP54JAORfYEK05mdHdQ4zfpMKfDUNJLnc5tk3H/AndroidLandscape.mp4\"
Content-Type: video/mp4


------WebKitFormBoundaryBEDAIwXzG6Ik2xVY
Content-Disposition: form-data; name=\"oOJGp433ODZvBOZTCXNz1oO7ogG0j3BRRBo98jpx1iIlvMPeNoc8nBKvpoTjx9PsOl5ulGGWniur3TdbDSd9TpgsnWhhqurcQO3TnssSQNHWti7xm7nZGW6tFRtrjrvwoJm9Bds5AsMcNKxT7oBkzA35fA1fgo5jkiUAfHHiduMdGIYf3NJGk8LP54JAORfYEK05mdHdQ4zfpMKfDUNJLnc5tk3H/Yym32tTMGQAfAMVGFTUJA1z9zQB3YremlDV1Hluotwj21UZWP9Aop6QTPvUMVIZVS8Hk6gADadVu4TihPloTy5N7JX99SgPqf3JZILRSMtEMCXLeT4gw34aq5e0HfxetOlKHTx6m2uS1SLFHi8OvcujtWEIAlTfXQW5pvsFGMJYOwNwWjncOoZETXaTs1LspDUHchPEHypp4CHEM5Y3e5HhsKBkA9cFJs6oA26XQW7y/AndroidPortrait.mp4\"; filename=\"oOJGp433ODZvBOZTCXNz1oO7ogG0j3BRRBo98jpx1iIlvMPeNoc8nBKvpoTjx9PsOl5ulGGWniur3TdbDSd9TpgsnWhhqurcQO3TnssSQNHWti7xm7nZGW6tFRtrjrvwoJm9Bds5AsMcNKxT7oBkzA35fA1fgo5jkiUAfHHiduMdGIYf3NJGk8LP54JAORfYEK05mdHdQ4zfpMKfDUNJLnc5tk3H/Yym32tTMGQAfAMVGFTUJA1z9zQB3YremlDV1Hluotwj21UZWP9Aop6QTPvUMVIZVS8Hk6gADadVu4TihPloTy5N7JX99SgPqf3JZILRSMtEMCXLeT4gw34aq5e0HfxetOlKHTx6m2uS1SLFHi8OvcujtWEIAlTfXQW5pvsFGMJYOwNwWjncOoZETXaTs1LspDUHchPEHypp4CHEM5Y3e5HhsKBkA9cFJs6oA26XQW7y/AndroidPortrait.mp4\"
Content-Type: video/mp4


------WebKitFormBoundaryBEDAIwXzG6Ik2xVY--

当我的 create() 方法收到请求时,我发现 request.FILES 只包含第一个文件 (AndroidLandscape.mp4)。第二个文件(AndroidPortrait.mp4)似乎被默默地丢弃了。

我怀疑这是由 parser.MultiPartParser 完成的,但我不确定。

是否因为路径太长而被丢弃?

(更新:我做了一些测试,470 个字符似乎是魔术路径长度限制。如果路径是 471 个字符或更长,则该文件不包含在 request.FILES 中)

如果上传路径不能那么长,我可以接受,但我需要检测这是否已经发生,以便我可以向客户端返回适当的错误响应,而不是默默地丢弃文件。如果是这样,我如何在我的方法中检测到这一点?

  • 通过与a w3c example 比较我可以看到: (1) 你可以尝试删除“名称”部分吗?文件名属性似乎没有必要。 (2) \"Content-Disposition\" 应该有视频的值 \"file\",我认为是 \"Content-Transfer-Encoding: binary\"。 (3) 缺少多部分声明(\"Content-Type: multipart/mixed;\")。 -- 任何这一点都可以促使解析器将其视为“畸形部分”;但你是对的,不应该默默地忽略它:/

标签: parsing file-upload django-rest-framework


【解决方案1】:

我终于找到了为什么会这样。 Django Rest Framework 代码的最大标头长度硬编码为 1024 字节。对于长文件路径,标头的大小太长,MultiPartParser 在 1024 字节后停止从流中读取。这导致标头无效,并且文件被丢弃。不幸的是,这种“溢出”被无声地吞噬,因此文件被无声地丢弃,开发人员的代码甚至无法知道它发生了,或者文件甚至被尝试上传。

我能够通过子类化/覆盖受影响的类,复制/粘贴受影响方法的库代码,最后将硬编码的 1024 字节限制更改为更高的数字来实现一个可行的解决方案。

这不是一个很好的解决方案,因为修补库代码很脆弱,并且该解决方案可能会导致 DRF 的未来版本发生冲突,但这是我目前看到的唯一解决方案。

如果有人想实现这个解决方案,带有硬编码限制的代码是这样的:

在 .../site-packages/django/http/multipartparser.py 中:

class Parser:
    def __init__(self, stream, boundary):
        self._stream = stream
        self._separator = b'--' + boundary

    def __iter__(self):
        boundarystream = InterBoundaryIter(self._stream, self._separator)
        for sub_stream in boundarystream:
            # Iterate over each part
            yield parse_boundary_stream(sub_stream, 1024)

最后一行中的 1024 必须增加到所需的最大标头长度。

【讨论】:

    猜你喜欢
    • 2023-03-30
    • 2012-05-18
    • 2010-12-31
    • 1970-01-01
    • 2011-01-21
    • 2020-10-13
    • 1970-01-01
    • 1970-01-01
    • 2014-05-26
    相关资源
    最近更新 更多