Requests: 生成没有文件的多部分帖子

创建于 2013-01-03  ·  36评论  ·  资料来源: psf/requests

目前,拥有多部分表单请求的唯一方法是r = requests.post(url, data=payload, files=files)
可能有一个组件

Content-Disposition: form-data; name="file"; filename="filename.txt"
Content-Type: text/plain

content
--3eeaadbfda0441b8be821bbed2962e4d--

但是,我遇到过帖子需要采用多部分格式而没有关联文件的情况,例如:

Content-Disposition: form-data; name="key1"

value1
--3eeaadbfda0441b8be821bbed2962e4d

但是没有前者,后者是不可能产生的。

也许我们可以添加一个像r = requests.post(url, data=payload, multipart=True)这样的标志来强制一个帖子是多部分的,即使没有文件。

如果这听起来是个好主意,我很乐意致力于实现这一点。

所有36条评论

这个之前已经讨论过了。 这将代表 API 的重大变化,我不确定@kennethreitz是否愿意。

就我个人而言,我更倾向于公开一个函数来从 API 中的字典(元组列表等)生成多部分数据,以便用户可以使用它并将生成的数据传递给请求。 表面上,如果他们不使用文件,就不应该有巨大的内存冲击,但即便如此,他们的内存中已经有一个巨大的字符串,第二个不会真正杀死他们,这将是他们的错,而不是我们的错.

也许@kennethreitz更适合第二种解决方案。 我认为它也不符合请求的设计理念,并且考虑到 API 的其余部分会非常奇怪,但是_耸肩_谁知道。

事实上,当前的 API 不支持发送文件以外的多部分数据,这是一件坏事。 参见问题 #935 和 shazow/urllib3/issues/120

也许我错过了一些东西,但这些变化对我来说似乎很小。 我分叉了存储库,并在此处对我的版本进行了拟议更改: https :
(如果有更好的方法来做到这一点让我知道?我对 github 比较陌生)。

它可能需要几个单元测试,并且需要更改 api.py 中的文档字符串,但是这样的事情合理吗?

更改的问题不在于实现起来很复杂,而在于 API 的差异。 在这个正在进行的讨论中,我有点犹豫:我认为有一种上传多部分表单数据的好方法可能很有用,但我也认为当前的files API 是一个非常好的 API . 我_不_认为将multipartRequest API 是可行的方法。

老实说,我个人更愿意通过内容类型标头来控制它,但这可能比我们目前所做的更容易出错和让新用户感到困惑 100 倍。 我也对此持观望态度,但如果不这样做,我仍然会犯错。

为什么? 如果我们接受此功能请求,则更有可能有人抱怨没有 json 参数。 而且我相信其他人可以想出更多他们希望添加的参数。 就像现在一样,API 完全按照它应该做的,而且几乎没有杂乱无章。 看待这一点的一种方式是 KISS。 这使得请求并返回一个很好的对象,使使用响应变得容易和自然。 它做广告宣传的事情,剩下的由您来做。 它确实宣传了多部分编码,并通过其文档化设计来实现。 它可能看起来很尴尬,但它被记录在案并且有效。

_(...) 有人会抱怨没有 json 参数_

这样的抱怨是没有根据的,因为 json 是application媒体类型( rfc4627 )的子类型,而不是multipart媒体类型( httpbis 草案 21

_可能看起来很别扭(...)_

这很尴尬,但它不应该。

在阅读了之前的评论后,我想重申我的立场: multipart/form-data是当前使用的最常见的多部分 MIME 类型(需要引用:)),不支持它是一种严重的遗漏。

@piotr-dobrogost:尽管我上面说了,但我觉得你刚刚提出的论点甚至有点令人信服。

请求不支持 MIME 类型,它支持用例。 这种观点使您上面的评论看起来很奇怪。 例如,关于没有 JSON 参数的抱怨将是因为上传 JSON 格式的数据非常普遍——在请求用户中可能比上传非文件多部分数据更常见。 争辩说我们不会提供它,因为“我们只是multipart的特殊情况子类型”,这似乎是一件奇怪的事情。

无论如何,问题的核心是:API 是这个库的重点。 如果你不能想出一个_漂亮_的方式来实现这个功能,它就不会发生。

这样的抱怨是没有根据的[原文如此]

@piotr-dobrogost 你对开发人员的未来比我更有希望。 除此之外,请求的参数是不准确的。 它需要被称为更像form_data不是multipart 。 正如您所注意到的,(虽然是间接的) multipart可以指代许多不同的媒体类型。

这很尴尬,但它不应该。

并非所有事情都可以优雅(即使在 python 中)。

并且不支持

但它是支持的。 但是,这一边,我们有dataapplication/x-www-form-urlencodedfiles (或files+data )为multipart/form-data ,为什么我们还需要另一个参数当我们拥有它时,只需multipart/form-data

您不需要组合使用datafiles来获得预期的结果。 您可以执行类似于: requests.post('http://example.com/', files=[('key1', 'param1'), ('key2', 'param2')])而无需在其中放置实际文件。

如果我们准确地说, form_data可能不是很明显,那么为什么不使用参数multipart_form_data ,但现在这也很笨拙。 它是明确的,是的,并且 PEP 8 要求明确,但现有的行为是有据可查的。 如果@kennethreitz确实决定接受此功能请求,则所有参数需要做的就是充当 files 参数的别名。 但考虑到该行为已经得到支持,我认为没有必要。

@sigmavirus24@Lukasa完美地总结了这一点。

@piotr-dobrogost 您的贡献受到赞赏,但不是您的语气。 请停止对我们的项目及其目标做出坚定的断言。

从我的角度来看,请求已经支持多部分帖子是非常令人沮丧的,但不让用户在不使用文件的情况下访问它。 @sigmavirus24建议只在其中粘贴一个文件是不够的。 如果尝试过类似的操作,我使用的 Web 应用程序将返回错误代码。

我怀疑这将在未来继续成为用户的问题,我有点困惑为什么似乎没有努力解决这个问题。

请参阅我对 #935 的回复

哦谢谢。 我很期待!

@卢卡萨

_Requests 不支持 MIME 类型,它支持用例。_

发送multipart/form-data数据是一个常见的用例。

_(...) 上传JSON格式的数据很常见(...)_

我们无法将发送 json 与发送multipart/form-data 。 发送 json 很容易; 您设置Content-type ,使用内置模块在一行中编码数据,就是这样。 发送multipart/form-data更加复杂,因为请求的正文必须具有特定的结构。 换句话说,发送 json 与 HTTP 一样透明,但发送multipart/form-data则不然。 由于 Requests 是 HTTP 库,它应该负责创建这样的结构。

_如果你不能想出一个漂亮的方式来实现这个功能,它就不会发生。_

使用当前的files参数来发送与文件无关的multipart/form-data在任何方面都不美观(它很丑),但它以某种方式设法进入代码库:)。 想出一些不那么难看的东西真的很容易:)

@西格玛病毒24

_(...) 但现有的行为有据可查。_

没有多少文档可以从糟糕的 API 中提取出好的 API。 API 越好,需要的文档就越少。

_你可以做一些类似于 (...)_

是的,但这是非常具有误导性和不直观的。 我在之前的评论中描述了为此使用files参数有什么问题。

底线:想出更好的东西,我们需要承认当前的 api 在发送multipart/form-data数据方面很糟糕。

@spacecase

_从我的角度来看,请求已经支持多部分帖子是非常令人沮丧的,但不让用户在不使用文件的情况下访问它_

我同意,这就是我创建问题 #935 的原因。

此问题已关闭。

原谅我@kennethreitz@spacecase我没有在该示例中的任何地方使用文件。 我使用 files 参数来做你想要的。 如果我使用了一个文件,你就会看到open('filename')

@sigmavirus24 ,不幸的是,这不是我想要的。 这与客户端是否正在读取文件无关。 这就是告诉服务器的内容。 在您的示例中,帖子正文是

Content-Disposition: form-data; name="key1"; filename="key1"
Content-Type: application/octet-stream

param1
--2f8732ee35564115a6c6e0c1032773e8
Content-Disposition: form-data; name="key2"; filename="key2"
Content-Type: application/octet-stream

param2
--2f8732ee35564115a6c6e0c1032773e8--

注意filename= 。 它告诉服务器一个文件正在发送。 在网络应用程序中,我处理这种不正确的行为会导致错误。

我很抱歉,但你这么冒昧地告诉我这正是我想要的,这冒犯了我。 我是来提供想法和帮助的,在这种情况下,听起来你也不想要。 那很好,但请不要让我觉得被轻视。

嗯,好像我记错了行为。 对于那个很抱歉。 我根本无意冒犯您或让您觉得自己被轻视。 这绝对足以让我动摇需要一种更好的方法来处理multipart/form-data ,但现在我仍然不同意 API 的想法一点也不优雅。

@spacecase请看我对#935 的回复。 事情会更好。 您的反馈很重要,非常感谢。 :)

@spacecase作为表明您的意见确实重要的一种姿态,请查看 sigmavirus24/requests-data-schemes 作为权宜之计。 您必须设置自己的Content-Type标头,但这可能会带来一些小麻烦。

谢谢@sigmavirus24 ,我会试试的。 看起来它会成功。
我很感激你不是故意冒犯我。 我对此感觉好多了,我不反对你或这个项目。

是的,在某些人看来我显得很傲慢,所以我想我需要完善我在互联网上写的内容。 我不明白,其他人也同意我的看法,但我正在努力。 我想我只需要一个足够大的样本量,就可以在我无意中意识到对人们的冒犯(除了我通过记住一些不可行的方式来使自己变得愚蠢)。

我有一个文件和一些要发布的键值。 那么发送这种多部分表单请求的正确方法是什么? 我希望你能帮助我。

Content-Disposition: form-data; name="up"; filename="aa.PNG"
Content-Type: image/png

file data
---------------------------7dee5302248e
Content-Disposition: form-data; name="exp"


-----------------------------7dee5302248e
Content-Disposition: form-data; name="ptext"

text
-----------------------------7dee5302248e
Content-Disposition: form-data; name="board"

DV_Studio
-----------------------------7dee5302248e--

I tried this way but only got a 504 error.
myfile=[('file',open('bb.jpg')),('exp','python'),('ptext',''),('board','DV_Studio')]
r = requests.post(url,files=myfile)

@deerstalker有问题请使用StackOverflow 。 不过,要回答您的问题,有一个项目正在进行中以解决此问题sigmavirus24/requests-toolbelt

另请注意,我之前已经在 StackOverflow 上回答过这个问题,您可以在这里看到。

我有同样的问题,即在没有文件的情况下生成多部分帖子。 目前,如果您执行requests.post(url, data=data_dict)requests.post(url, data=data_dict, files={})没有区别。 但是,由于files关键字默认为None ,我们应该能够有两种不同的行为。 指定multipart/form-datafiles={}未指定时application/x-www-form-urlencoded

我错过了什么?

@jwoillez您是否关注了我发布的堆栈溢出链接?

我认为是这样,但是您在那边的回答涉及一个文件的 Multipart POST。 我回到原来的问题:没有文件的多部分 POST。

文件对象可以是字符串。 =) 那应该为您提供您想要的信息。

但是如果我按照你的建议去做,我会不会得到这样的结果:

Content-Disposition: form-data; name="file"; filename="filename.txt"
Content-Type: text/plain

content
--3eeaadbfda0441b8be821bbed2962e4d--

content是您邀请我使用的字符串而不是文件?

我真的只追求这个:

Content-Disposition: form-data; name="key1"

value1
--3eeaadbfda0441b8be821bbed2962e4d

您不需要的元组中的字段可以保留为默认值:

files = {'name': ('', 'content')}

将来,请将您的问题直接发送到 StackOverflow。 所有的维护人员都会定期跟踪它,这是提出这些问题的更合适的地方。

这就是我要找的答案,谢谢。 对不起,噪音。

也许最后一个问题,是否有以下可能(指定空文件名,空内容)?

Content-Disposition: form-data; name="file"; filename=""
Content-Type: text/plain


--3eeaadbfda0441b8be821bbed2962e4d--

您可以通过在元组的内容部分中使用空字符串来提供空内容。 您不能提供文字空文件名,但应以完全相同的方式处理不存在的文件名。

由于各种奇怪的原因,我偶尔需要这样做。
对于想要将 API 强加到这个用例中的任何人,我建议采用这种方法:

class ForceMultipartDict(dict):
    def __bool__(self):
        return True


FORCE_MULTIPART = ForceMultipartDict()  # An empty dict that boolean-evaluates as `True`.


client.post("/", data={"some": "data"}, files=FORCE_MULTIPART)

或者你可以使用工具带而不是求助于黑客。

此页面是否有帮助?
0 / 5 - 0 等级