利用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的collections模块中的deque双端队列结构
Jul 07 Python
python http接口自动化脚本详解
Jan 02 Python
Python面向对象类继承和组合实例分析
May 28 Python
Python(TensorFlow框架)实现手写数字识别系统的方法
May 29 Python
浅谈python中拼接路径os.path.join斜杠的问题
Oct 23 Python
Tensorflow训练MNIST手写数字识别模型
Feb 13 Python
python爬虫学习笔记之Beautifulsoup模块用法详解
Apr 09 Python
Python使用monkey.patch_all()解决协程阻塞问题
Apr 15 Python
python输出数学符号实例
May 11 Python
python等待10秒执行下一命令的方法
Jul 19 Python
python Tornado框架的使用示例
Oct 19 Python
Python读取ini配置文件传参的简单示例
Jan 05 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入库和出库
2013/06/25 PHP
php输出1000以内质数(素数)示例
2014/02/16 PHP
JavaScript脚本性能的优化方法
2007/02/02 Javascript
网上抓的一个特效
2007/05/11 Javascript
JavaScript 编写匿名函数的几种方法
2010/02/21 Javascript
jQuery EasyUI API 中文文档 - NumberSpinner数值微调器使用介绍
2011/10/21 Javascript
js日期相关函数总结分享
2013/10/15 Javascript
jquery prop的使用介绍及与attr的区别
2013/12/19 Javascript
js实现动态改变字体大小代码
2014/01/02 Javascript
JS嵌套函数调用上下文的问题解决
2014/03/26 Javascript
JS 获取鼠标左右键的键值方法
2014/10/11 Javascript
jQuery实现表格行上下移动和置顶效果
2015/06/05 Javascript
基于JavaScript实现网页倒计时自动跳转代码
2015/12/28 Javascript
JavaScript学习笔记整理_关于表达式和语句
2016/09/19 Javascript
Angularjs通过指令监听ng-repeat渲染完成后执行脚本的方法
2016/12/31 Javascript
xmlplus组件设计系列之按钮(2)
2017/04/26 Javascript
JS判断数组那点事
2017/10/10 Javascript
vue路由事件beforeRouteLeave及组件内定时器的清除方法
2018/09/29 Javascript
Vue源码中要const _toStr = Object.prototype.toString的原因分析
2018/12/09 Javascript
微信小程序用户授权,以及判断登录是否过期的方法
2019/05/10 Javascript
Vue3为什么这么快
2020/09/23 Javascript
[02:32]DOTA2英雄基础教程 祸乱之源
2013/12/23 DOTA
Python多线程学习资料
2012/12/19 Python
python分析作业提交情况
2017/11/22 Python
Python2/3中urllib库的一些常见用法
2017/12/19 Python
python3解析库pyquery的深入讲解
2018/06/26 Python
PyGame贪吃蛇的实现代码示例
2018/11/21 Python
Python3实现腾讯云OCR识别
2018/11/27 Python
Python实现读取txt文件中的数据并绘制出图形操作示例
2019/02/26 Python
python实现手势识别的示例(入门)
2020/04/15 Python
Python控制鼠标键盘代码实例
2020/12/08 Python
PHP面试题及答案一
2012/06/18 面试题
2016年中学清明节活动总结
2016/04/01 职场文书
关于销售人员的年终工作总结要点
2019/08/15 职场文书
Pytorch中Softmax与LogSigmoid的对比分析
2021/06/05 Python
python创建字典及相关管理操作
2022/04/13 Python