利用python模拟实现POST请求提交图片的方法
作者:Roy 发布时间:2021-02-20 05:58:21
本文主要给大家介绍的是关于利用python模拟实现POST请求提交图片的方法,分享出来供大家参考学习,下面来一看看详细的介绍:
使用requests来模拟HTTP请求本来是一件非常轻松的事情,比如上传图片来说,简单的几行代码即可:
import requests
files = {'attachment_file': ('1.png', open('1.png', 'rb'), 'image/png', {})}
values = {'next':"http://www.xxxx.com/xxxx"}
r = requests.post('http://www.xxxx.com/upload', files=files, data=values) # 成功
r = requests.post('http://www.xxxx.com/upload', files=files, data=values) # 失败
r = requests.post('http://www.xxxx.com/upload', files=files, data=values) # 失败
r = requests.post('http://www.xxxx.com/upload', files=files, data=values) # 失败
r = requests.post('http://www.xxxx.com/upload', files=files, data=values) # 失败
...
不过我今天在调试一个django程序的时候却遇到了大坑————为了偷懒,我直接在ipython中执行了上述代码,第一次提交的时候一切正常,但第二次之后提交就怎么也通过不了django的form验证。
验证部分的代码很简单:
......
form = AttachmentForm(request.POST, request.FILES)
if form.is_valid():
form.save(request, obj)
messages.success(request,_('Your attachment was uploaded.'))
return HttpResponseRedirect(next)
......
什么鬼!?怎么只有第一次成功提交???后面全失败??只好一步一步的跟进到django源码中,发现问题出在django/forms/fields.py文件中:
def to_python(self, data):
if data in validators.EMPTY_VALUES:
return None
# UploadedFile objects should have name and size attributes.
try:
file_name = data.name
file_size = data.size
except AttributeError:
raise ValidationError(self.error_messages['invalid'])
if self.max_length is not None and len(file_name) > self.max_length:
error_values = {'max': self.max_length, 'length': len(file_name)}
raise ValidationError(self.error_messages['max_length'] % error_values)
if not file_name:
raise ValidationError(self.error_messages['invalid'])
if not self.allow_empty_file and not file_size:
raise ValidationError(self.error_messages['empty'])
return data
在第一次执行的时候,一切正常,这个data即InMemoryUploadFile文件类型,name、size就是我们上传的图片名、大小,而第二次执行post请求时候,发现data.size
居然变成了0?!怪不得直接引发了if not self.allow_empty_file and not file_size这
个判断的异常呢!
由此可知,问题的核心并不出现在django对于表单验证的部分,而是出自发送请求的部分。不过发请求的部分代码很简单啊?分别输出了正常情况和错误情况requests发出的请求包,发现区别了:
#正常情况
In [28]: r = requests.post('http://www.xxxx.com/upload', files=files, data=values)
In [29]: r.request.body
#错误情况
In [33]: r = requests.post('http://www.xxxx.com/upload', files=files, data=values)
In [34]: r.request.body
Out[34]: '--155322d3e780432bb06e58135e041c8f\r\nContent-Disposition: form-data; name="next"\r\n\r\nhttp://www.xxxx.com/upload\r\n--155322d3e780432bb06e58135e041c8f\r\nContent-Disposition: form-data; name="attachment_file"; filename="1.png"\r\nContent-Type: image/png\r\n\r\n\r\n--155322d3e780432bb06e58135e041c8f--\r\n'
正常情况没输出,错误情况反而看着像正常情况下的输出?这不科学啊?
结合以上2点,我隐约感觉问题出在数据的构造上,关键在于files = {'attachment_file': ('1.png', open('1.png', 'rb') , 'image/png', {})}
这里,首先关于字典、列表这种可变类型作为函数的参数传递时候就需要特别注意,其次open函数打开了一个文件,那么哪里关闭文件了呢?
带着这个怀疑,我把代码改写成:
fl = open('1.png','rb')
files = {'attachment_file': ('1.png', fl, 'image/png', {})}
r1 = requests.post('http://www.xxxx.com/upload', files=files, data=values)
fl.close()
fl = open('1.png','rb')
files = {'attachment_file': ('1.png', fl, 'image/png', {})}
r2 = requests.post('http://www.xxxx.com/upload', files=files, data=values)
然后再执行,果然成功上传了2张图片。其实按照正常情况不会出现测试时候这种打开一张图片不停上传的情形,不过也正因为这样才会遇到如此有意思的问题。关于requests中files对象的处理代码在models.py文件中,有兴趣的读者可以自行调试。
另外,requests调用时上传文件名中不能包含中文,否则也不能通过django表单验证,这里也不深究原因了。
来源:http://www.hi-roy.com/2016/09/14/使用python模拟POST请求提交图片/
猜你喜欢
- 找到给定二叉树的最小深度最小深度是从根节点到最近叶子节点的最短路径上的节点数量注意:叶子节点没有子树Example:Given binary
- 实例: <?php $conn1 = mysql_connect("127.0.0.1", "root&
- 当然可以,我们使用强大的fso对象来获取文件夹的大小请敲入如下代码即可:<%Set MyFileSize =&nb
- 前言本来准备讲解nginx和apache的日志的,但是个人不太推荐apache(纯属个人爱好),这里就不介绍apache的日志了。作为一名程
- 先看一个需求from collections import defaultdict"""需求: 统计user_
- 利用FSO取得BMP,JPG,PNG,GIF文件信息:大小,宽、高尺寸等 ’:::::::::::::::::::::::::::
- 如何使用快捷键按出代码提示框更新win10,发现可以改取消ctrl + space快捷键的占用了!!!我们在平时写代码的时候难免会出现敲错字
- 还有多少耿直boy和我一样在等待微信官方送上一顶圣诞帽?最后知道真相的我眼泪掉下来……(还蒙在鼓里的同学请在微信最上方的搜索栏自行搜索『圣诞
- 1、安装所需工具pip install pipreqs2、进入到python项目主目录pipreqs ./3、完成上面命令会生成requir
- 密码学俱乐部的第一条规则是:永远不要自己发明密码系统。密码学俱乐部的第二条规则是:永远不要自己实现密码系统:在现实世界中,在实现以及设计密码
- pip的安装,以及使用pip安装包的方法,记录如下,分享给大家:—–安装python的时候勾选了下载pip,不知道为什么没下载。然后就偷懒想
- Python 中提供了对时间日期的多种多样的处理方式,主要是在 time 和 datetime 这两个模块里。一、time 模块time 模
- 在 Internet 连接无处不在的今天,我们忽然有了另外一个需求,离线 Web。Gmail, Google Reader, Zoho 这些
- 主要步骤1.生成普通python数组(bytearray(),os.urandom())2.转换成numpy数组(numpy.array()
- 引用计数在Python源码中,每一个对象都是一个结构体表示,都有一个计数字段。typedef struct_object { i
- 前言又要过年了,今年你不妨自己写一段代码来抢回家的火车票,是不是很Cool。下面话不多说了,来一起看看详细的介绍吧。先准备好:12306网站
- 一、前言 JDK(Java Development Kit )是一切java应用程序的基础,可以说,所有的java应用程序是构建
- Python对递归函数设置是有默认值。 可以通过下面命令来查看设置的默认值>>> import sys>>&g
- 当我们想复制两个一模一样的列表时,我们可能使用到list.copy()这个方法,这个方法可以让我们复制一个相同的数组,当遇到下面这种情况时,
- 这篇文章主要介绍了python的time模块和datetime模块实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参