python - 如何在 Django 中通过 PUT 请求处理文件上传?

标签 python django rest http-put django-1.3

我正在实现一个 REST 风格的界面,并希望能够通过 HTTP PUT 请求创建(通过上传)文件。我想创建一个 TemporaryUploadedFile 或一个 InMemoryUploadedFile 然后我可以将其传递给我现有的 FileField.save() 在作为模型一部分的对象上,从而存储文件。

我不太确定如何处理文件上传部分。具体来说,这是一个 put 请求,我无权访问 request.FILES,因为它在 PUT 请求中不存在。

所以,一些问题:

  • 我可以利用 HttpRequest 类中的现有功能,特别是处理文件上传的部分吗?我知道直接 PUT 不是多部分 MIME 请求,所以我不这么认为,但值得一问。
  • 如何推断所发送内容的 MIME 类型?如果我没看错的话,PUT 主体就是没有前奏的文件。因此,我是否要求用户在其 header 中指定 MIME 类型?
  • 如何将其扩展到大量数据?我不想将它全部读入内存,因为那是非常低效的。理想情况下,我会做 TemporaryUploadFile 和相关代码所做的事情 - 一次写一部分?

我看过 this code sample这会欺骗 Django 将 PUT 作为 POST 请求进行处理。如果我做对了,它只会处理表单编码数据。这是 REST,因此最好的解决方案是不假设存在表单编码数据。不过,我很高兴听到有关以某种方式使用 mime(而不是 multipart)的适当建议(但上传应该只包含一个文件)。

Django 1.3 是可以接受的。所以我可以使用 request.raw_post_datarequest.read() (或者其他更好的访问方法)来做一些事情。有什么想法吗?

最佳答案

Django 1.3 is acceptable. So I can either do something with request.raw_post_data or request.read() (or alternatively some other better method of access). Any ideas?

您不想接触 request.raw_post_data - 这意味着将整个请求正文读取到内存中,如果您正在谈论文件上传可能会非常大,所以 request.read() 是要走的路。您也可以使用 Django <= 1.2 来执行此操作,但这意味着在 HttpRequest 中挖掘以找出使用私有(private)接口(interface)的正确方法,然后确保您的代码能够也兼容 Django >= 1.3。

我建议您要做的是复制 existing file upload behaviour parts of the MultiPartParser class :

  1. request.upload_handlers 获取上传处理程序(默认为 MemoryFileUploadHandler & TemporaryFileUploadHandler)
  2. 确定请求的内容长度(在 HttpRequestMultiPartParser 中搜索 Content-Length 以查看执行此操作的正确方法。)
  3. 确定上传文件的文件名,方法是让客户端使用 url 的最后一个路径部分指定它,或者让客户端在 the Content-Disposition header 的“filename=”部分指定它.
  4. 对于每个处理程序,使用相关参数调用 handler.new_file(模拟字段名称)
  5. 使用 request.read() 分 block 读取请求正文,并为每个 block 调用 handler.receive_data_chunk()
  6. 对于每个处理程序调用handler.file_complete(),如果它返回一个值,那就是上传的文件。

How can I deduce the mime type of what is being sent? If I've got it right, a PUT body is simply the file without prelude. Do I therefore require that the user specify the mime type in their headers?

要么让客户端在 Content-Type header 中指定它,要么使用 python's mimetype module猜测媒体类型。

我很想知道你是如何处理这件事的——这是我一直想要调查自己的事情,如果你能发表评论让我知道进展如何,那就太好了!


按要求由 Ninefingers 编辑,这是我所做的,完全基于上述内容和 django 源代码。

upload_handlers = request.upload_handlers
content_type   = str(request.META.get('CONTENT_TYPE', ""))
content_length = int(request.META.get('CONTENT_LENGTH', 0))

if content_type == "":
    return HttpResponse(status=400)
if content_length == 0:
    # both returned 0
    return HttpResponse(status=400)

content_type = content_type.split(";")[0].strip()
try:
    charset = content_type.split(";")[1].strip()
except IndexError:
    charset = ""

# we can get the file name via the path, we don't actually
file_name = path.split("/")[-1:][0]
field_name = file_name

由于我在此处定义 API,因此无需担心跨浏览器支持。就我的协议(protocol)而言,不提供正确的信息是一个错误的请求。我对是否要说 image/jpeg; 有两种看法。 charset=binary 或者如果我要允许不存在的字符集。无论如何,我将设置 Content-Type 有效地作为客户端的责任。

同样,对于我的协议(protocol),文件名是传入的。我不确定field_name参数是干什么用的,来源也没有给出太多线索。

下面发生的事情实际上比看起来要简单得多。您询问每个处理程序是否会处理原始输入。正如上述作者所说,您默认拥有 MemoryFileUploadHandlerTemporaryFileUploadHandler。好吧,事实证明 MemoryFileUploadHandler 将在被要求创建一个 new_file 时决定它是否会处理该文件(基于各种设置)。如果它决定要这样做,它会抛出一个异常,否则它不会创建文件并让另一个处理程序接管。

我不确定 counters 的用途是什么,但我从源头上保留了它。其余的应该是直截了当的。

counters = [0]*len(upload_handlers)

for handler in upload_handlers:
    result = handler.handle_raw_input("",request.META,content_length,"","")

for handler in upload_handlers:

    try:
        handler.new_file(field_name, file_name, 
                         content_type, content_length, charset)
    except StopFutureHandlers:
        break

for i, handler in enumerate(upload_handlers):
    while True:
        chunk = request.read(handler.chunk_size)
        if chunk:

            handler.receive_data_chunk(chunk, counters[i])
            counters[i] += len(chunk)
        else:
            # no chunk
            break

for i, handler in enumerate(upload_handlers):
    file_obj = handler.file_complete(counters[i])
    if not file_obj:
        # some indication this didn't work?
        return HttpResponse(status=500) 
    else:
        # handle file obj!

关于python - 如何在 Django 中通过 PUT 请求处理文件上传?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5731984/

相关文章:

python - 如何在 Django Admin 中仅折叠一个字段?

有理由的 RESTful DELETE

rest - 无法为 Azure 文件创建有效的共享访问签名

javascript - 如何通过 REST 使用 Kinvey 执行 'Hello World'?

python - 如何将本地 conda env 中的 python 脚本转换为 Linux 中的 systemd 服务?

python - 使用 PIL 复制 ImageMagicks 'Badge using Lighting Effects'

python - 如何在 Django CRUD 中自定义 auth.User 管理页面?

Django - 在 request.session 中存储查询集仍然查询数据库 - 为什么?

python - Python API 设计中的重载(或替代方案)

java - 执行公正的程序/脚本性能比较