如何通过Django中的PUT请求处理文件上传? [英] How do I handle file upload via PUT request in Django?

查看:157
本文介绍了如何通过Django中的PUT请求处理文件上传?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm implementing a REST-style interface and would like to be able to create (via upload) files via a HTTP PUT request. I would like to create either a TemporaryUploadedFile or a InMemoryUploadedFile which I can then pass to my existing FileField and .save() on the object that is part of the model, thereby storing the file.

我不太清楚如何处理文件上传部分。具体来说,这是一个放置请求,我没有访问 request.FILES ,因为它不存在于 PUT 请求。

I'm not quite sure about how to handle the file upload part. Specifically, this being a put request, I do not have access to request.FILES since it does not exist in a PUT request.

所以,一些问题:


  • 我可以利用现有的功能在 HttpRequest 类中,特别是处理文件上传的部分?我知道一个直接的 PUT 不是一个多部分MIME请求,所以我不这么认为,但值得一提。

  • 如何推断出发送的MIME类型?如果我说得对,一个PUT的身体就是没有前奏的文件。因此,我是否要求用户在其标题中指定mime类型?

  • 如何将其扩展到大量数据?我不想把它全部读入内存,因为这是非常低效的。理想情况下,我会做一些 TemporaryUploadFile 和相关的代码 - 一次写入它?

  • Can I leverage existing functionality in the HttpRequest class, specifically the part that handles file uploads? I know a direct PUT is not a multipart MIME request, so I don't think so, but it is worth asking.
  • 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?
  • How do I extend this to large amounts of data? I don't want to read it all into memory since that is highly inefficient. Ideally I'd do what TemporaryUploadFile and related code does - write it part at a time?

我已经看过这个代码示例,它使Django将 PUT 作为 POST 请求处理。如果我知道它是正确的,它只会处理表单编码数据。这是REST,所以最好的解决方案是不要假设表单编码数据将存在。但是,我很高兴听到有关使用mime(不是multipart)的一些方式(但上传应该只包含一个文件)的适当建议。

I've taken a look at this code sample which tricks Django into handling PUT as a POST request. If I've got it right though, it'll only handle form encoded data. This is REST, so the best solution would be to not assume form encoded data will exist. However, I'm happy to hear appropriate advice on using mime (not multipart) somehow (but the upload should only contain a single file).

Django 1.3是可以接受的。所以我可以使用 request.raw_post_data request.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?

推荐答案


Django 1.3是可以接受的。所以我可以
或者做一些
request.raw_post_data或
request.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 中挖掘,以找出使用私有接口的正确方法,这是一个真实的拖动,然后确保你的代码也将兼容Django> = 1.3。

You don't want to be touching request.raw_post_data - that implies reading the entire request body into memory, which if you're talking about file uploads might be a very large amount, so request.read() is the way to go. You can do this with Django <= 1.2 as well, but it means digging around in HttpRequest to figure out the the right way to use the private interfaces, and it's a real drag to then ensure your code will also be compatible with Django >= 1.3.

我建议你想做的是复制现有文件上传行为部分的 MultiPartParser class

I'd suggest that what you want to do is to replicate the existing file upload behaviour parts of the MultiPartParser class:


  1. request.upload_handlers (默认情况下为$ code)MemoryFileUploadHandler & TemporaryFileUploadHandler

  2. 确定请求的内容长度(在 HttpRequest MultiPartParser 中查找内容长度以查看正确的方法。 )

  3. 确定上传的文件的文件名,方法是让clien t使用URL的最后一个路径部分来指定,或者通过让客户端在 Content-Disposition 标题

  4. 对于每个处理程序,调用处理程序

  5. 使用 request.read()<>读取大块中的请求正文; .new_file 与相关的参数(嘲弄字段名称) / code>并为每个块调用 handler.receive_data_chunk()

  6. 对于每个处理程序调用 handler.file_complete(),如果它返回一个值,那就是上传的文件。

  1. Retrieve the upload handers from request.upload_handlers (Which by default will be MemoryFileUploadHandler & TemporaryFileUploadHandler)
  2. Determine the request's content length (Search of Content-Length in HttpRequest or MultiPartParser to see the right way to do this.)
  3. Determine the uploaded file's filename, either by letting the client specify this using the last path part of the url, or by letting the client specify it in the "filename=" part of the Content-Disposition header.
  4. For each handler, call handler.new_file with the relevant args (mocking up a field name)
  5. Read the request body in chunks using request.read() and calling handler.receive_data_chunk() for each chunk.
  6. For each handler call handler.file_complete(), and if it returns a value, that's the uploaded file.




如何推断出
正在发送的MIME类型?如果我说得对,一个
PUT机身就是没有
前奏的文件。因此,我是否要求
用户在
中指定mime类型的标题?

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标题中指定,或者使用 python的mimetype模块来猜测媒体类型。

Either let the client specify it in the Content-Type header, or use python's mimetype module to guess the media type.

我有兴趣了解如何实现这一点 - 这是我一直认为自己的东西,如果你可以评论让我知道如何去!

I'd be interested to find out how you get on with this - it's something I've been meaning to look into myself, be great if you could comment to let me know how it goes!

根据要求编辑Ninefingers ,这是我完全基于上述和django源。

Edit by Ninefingers as requested, this is what I did and is based entirely on the above and the django source.

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,因此跨浏览器支持不是问题。就我的协议来说,不提供正确的信息是一个破坏的请求。我在两个想法中,我是否要说 image / jpeg; charset = binary 或者我要允许不存在的字符集。无论如何,我将客户端的责任有效地设置为 Content-Type

Since I'm defining the API here, cross browser support isn't a concern. As far as my protocol is concerned, not supplying the correct information is a broken request. I'm in two minds as to whether I want say image/jpeg; charset=binary or if I'm going to allow non-existent charsets. In any case, I'm putting setting Content-Type validly as a client-side responsibility.

同样,对于我的协议,文件名被传入。我不知道 field_name 参数是什么,源没有提供很多线索。

Similarly, for my protocol, the file name is passed in. I'm not sure what the field_name parameter is for and the source didn't give many clues.

下面发生的事情实际上比看起来简单得多。你会询问每个处理程序是否处理原始输入。作为上述状态的作者,您已经获得了 MemoryFileUploadHandler & TemporaryFileUploadHandler 默认情况下。那么,当被要求创建一个 new_file 决定是否会处理该文件(基于...)时,会发生 MemoryFileUploadHandler 在各种设置)。如果它决定它会发生异常,否则它将不会创建文件,并允许另一个处理程序接管。

What happens below is actually much simpler than it looks. You ask each handler if it will handle the raw input. As the author of the above states, you've got MemoryFileUploadHandler & TemporaryFileUploadHandler by default. Well, it turns out MemoryFileUploadHandler will when asked to create a new_file decide whether it will or not handle the file (based on various settings). If it decides it's going to, it throws an exception, otherwise it won't create the file and lets another handler take over.

我不知道什么目的的计数器是,但我把它从源头。其余的应该是直截了当的。

I'm not sure what the purpose of counters was, but I've kept it from the source. The rest should be straightforward.

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!

这篇关于如何通过Django中的PUT请求处理文件上传?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆