利用python模拟实现POST请求提交图片的方法


Posted in Python onJuly 25, 2017

本文主要给大家介绍的是关于利用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表单验证,这里也不深究原因了。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Python 相关文章推荐
Python Mysql数据库操作 Perl操作Mysql数据库
Jan 12 Python
详解Python的Flask框架中的signals信号机制
Jun 13 Python
Python新手们容易犯的几个错误总结
Apr 01 Python
python 调用c语言函数的方法
Sep 29 Python
深入理解Django中内置的用户认证
Oct 06 Python
Python测试人员需要掌握的知识
Feb 08 Python
python 读取目录下csv文件并绘制曲线v111的方法
Jul 06 Python
Python开发的十个小贴士和技巧及长常犯错误
Sep 27 Python
利用Python查看微信共同好友功能的实现代码
Apr 24 Python
Python 内置函数globals()和locals()对比详解
Dec 23 Python
python求最大公约数和最小公倍数的简单方法
Feb 13 Python
详解Python直接赋值,深拷贝和浅拷贝
Jul 09 Python
利用django如何解析用户上传的excel文件
Jul 24 #Python
Python编程之变量赋值操作实例分析
Jul 24 #Python
Python模块结构与布局操作方法实例分析
Jul 24 #Python
Python列表list操作符实例分析【标准类型操作符、切片、连接字符、列表解析、重复操作等】
Jul 24 #Python
Python列表list内建函数用法实例分析【insert、remove、index、pop等】
Jul 24 #Python
python引入导入自定义模块和外部文件的实例
Jul 24 #Python
Python3.X 线程中信号量的使用方法示例
Jul 24 #Python
You might like
学习使用PHP数组
2006/10/09 PHP
如何使用动态共享对象的模式来安装PHP
2006/10/09 PHP
PHP令牌 Token改进版
2008/07/18 PHP
destoon实现调用热门关键字的方法
2014/07/15 PHP
php根据一个给定范围和步进生成数组的方法
2015/06/19 PHP
PHP请求远程地址设置超时时间的解决方法
2016/10/29 PHP
Laravel实现定时任务的示例代码
2017/08/10 PHP
PHP实现随机数字、字母的验证码功能
2018/08/01 PHP
JS中的substring和substr函数的区别说明
2013/05/07 Javascript
纯CSS3代码实现滑动开关效果
2015/08/19 Javascript
Javascript json object 与string 相互转换的简单实现
2016/09/27 Javascript
jQuery中弹出iframe内嵌页面元素到父页面并全屏化的实例代码
2016/12/27 Javascript
JavaScript表单验证完美代码
2017/03/02 Javascript
Flask中获取小程序Request数据的两种方法
2017/05/12 Javascript
详解windows下vue-cli及webpack 构建网站(二)导入bootstrap样式
2017/06/17 Javascript
React Native时间转换格式工具类分享
2017/10/24 Javascript
jQuery实现的老虎机跑动效果示例
2018/12/29 jQuery
详解小程序毫秒级倒计时(适用于拼团秒杀功能)
2019/05/05 Javascript
跟老齐学Python之眼花缭乱的运算符
2014/09/14 Python
在Django的模型中添加自定义方法的示例
2015/07/21 Python
Python使用正则表达式实现文本替换的方法
2017/04/18 Python
python reduce 函数使用详解
2017/12/05 Python
pandas计算最大连续间隔的方法
2019/07/04 Python
python 使用openpyxl读取excel数据
2021/02/18 Python
CSS3的calc()做响应模式布局的实现方法
2017/09/06 HTML / CSS
利用CSS3 动画 绘画 圆形动态时钟
2018/03/20 HTML / CSS
node中使用shell脚本的方法步骤
2021/03/23 Javascript
酒店前厅员工辞职信
2014/01/08 职场文书
有关打架的检讨书
2014/01/25 职场文书
总经理文秘岗位职责
2014/02/03 职场文书
中国梦演讲稿开场白
2014/08/28 职场文书
工程项目经理岗位职责
2015/02/02 职场文书
荒岛余生观后感
2015/06/09 职场文书
MySQL如何构建数据表索引
2021/05/13 MySQL
详解Laravel制作API接口
2021/05/31 PHP
Redis实现分布式锁的五种方法详解
2022/06/14 Redis