如何在 Django 中通过 PUT 请求处理文件上传?
我正在实现 REST 风格的界面,并且希望能够通过 HTTP PUT 请求创建(通过上传)文件。我想创建一个 TemporaryUploadedFile
或 InMemoryUploadedFile
,然后我可以将其传递给我现有的 FileField
和 .save()< /code> 作为模型一部分的对象,从而存储文件。
我不太确定如何处理文件上传部分。具体来说,这是一个 put 请求,我无权访问 request.FILES
,因为它不存在于 PUT
请求中。
那么,有一些问题:
- 我可以利用 HttpRequest 类中的现有功能,特别是处理文件上传的部分吗?我知道直接的 PUT 不是多部分 MIME 请求,所以我不这么认为,但值得一问。
- 如何推断正在发送的内容的 MIME 类型?如果我没理解错的话,PUT 主体就是没有前奏的文件。因此,我是否要求用户在其标头中指定 mime 类型?
- 如何将其扩展到大量数据?我不想将其全部读入内存,因为效率非常低。理想情况下,我会做 TemporaryUploadFile 和相关代码所做的事情 - 一次写一部分?
我查看了此代码示例 欺骗 Django 将 PUT
作为 POST
请求进行处理。如果我做对了,它只会处理表单编码数据。这是 REST,因此最好的解决方案是不假设存在形式编码数据。但是,我很高兴听到有关以某种方式使用 mime(而不是多部分)的适当建议(但上传应该只包含单个文件)。
Django 1.3 是可以接受的。因此,我可以使用 request.raw_post_data
或 request.read()
(或者其他更好的访问方法)执行某些操作。有什么想法吗?
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.
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.
So, some questions:
- Can I leverage existing functionality in the
HttpRequest
class, specifically the part that handles file uploads? I know a directPUT
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?
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 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?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您不想接触
request.raw_post_data
- 这意味着将整个请求正文读取到内存中,如果您谈论的是文件上传,这可能是一个非常大的量,所以request .read()
是正确的选择。您也可以使用 Django <= 1.2 执行此操作,但这意味着深入研究HttpRequest
以找出使用私有接口的正确方法,并且确保您的代码确实很麻烦还将兼容 Django >= 1.3。我建议您要做的是复制 MultiPartParser 类的现有文件上传行为部分:
request.upload_handlers
检索上传处理程序(默认情况下为MemoryFileUploadHandler &
TemporaryFileUploadHandler
)HttpRequest
或MultiPartParser
中搜索 Content-Length 以查看执行此操作的正确方法。 )Content-Disposition
标头。handler.new_file
(模拟字段名称)request.read()
并调用分块读取请求正文每个块的 handler.receive_data_chunk()
。handler.file_complete()
,如果它返回一个值,则该值就是上传的文件。要么让客户端在 Content-Type 标头中指定它,要么使用 python 的 mimetype 模块猜测媒体类型。
我很想知道你是如何处理这件事的——这是我一直想审视自己的事情,如果你能发表评论让我知道事情进展如何,那就太好了!
按要求由 Ninefingers 编辑,这就是我所做的,完全基于上述内容和 django 源代码。
由于我在这里定义 API,因此跨浏览器支持不是问题。就我的协议而言,不提供正确的信息是一个错误的请求。对于是否要说 image/jpeg,我有两种想法; charset=binary 或者如果我要允许不存在的字符集。无论如何,我将有效设置
Content-Type
作为客户端责任。同样,对于我的协议,传入文件名。我不确定
field_name
参数的用途,并且源代码没有提供太多线索。下面发生的事情实际上比看起来简单得多。您询问每个处理程序是否会处理原始输入。正如上述作者所述,您已经有了
MemoryFileUploadHandler
&默认情况下TemporaryFileUploadHandler
。好吧,事实证明,当要求创建 new_file 时,MemoryFileUploadHandler
将决定是否处理该文件(基于各种设置)。如果它决定这样做,它会抛出异常,否则它不会创建文件并让另一个处理程序接管。我不确定计数器的目的是什么,但我没有透露它的来源。其余的应该很简单。
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, sorequest.read()
is the way to go. You can do this with Django <= 1.2 as well, but it means digging around inHttpRequest
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.I'd suggest that what you want to do is to replicate the existing file upload behaviour parts of the
MultiPartParser
class:request.upload_handlers
(Which by default will beMemoryFileUploadHandler
&TemporaryFileUploadHandler
)HttpRequest
orMultiPartParser
to see the right way to do this.)Content-Disposition
header.handler.new_file
with the relevant args (mocking up a field name)request.read()
and callinghandler.receive_data_chunk()
for each chunk.handler.file_complete()
, and if it returns a value, that's the uploaded file.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!
Edit by Ninefingers as requested, this is what I did and is based entirely on the above and the django source.
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 settingContent-Type
validly as a client-side responsibility.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.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 outMemoryFileUploadHandler
will when asked to create anew_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.由于 https://gist.github.com/g00fy,较新的 Django 版本允许更轻松地处理此问题-/1161423
我修改了给定的解决方案,如下所示:
能够像 POST 中那样访问文件和其他数据。如果您希望数据是只读的,您可以删除对
.dict()
的调用。Newer Django versions allow for handling this a lot easier thanks to https://gist.github.com/g00fy-/1161423
I modified the given solution like this:
to be able to access files and other data like in POST. You can remove the calls to
.dict()
if you want your data to be read-only.我在使用 Django 2.2 时遇到了这个问题,并且正在寻找仅适用于通过 PUT 请求上传文件的东西。
I hit this problem while working with Django 2.2, and was looking for something that just worked for uploading a file via PUT request.