利用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的package机制如何简化utils包设计详解
Dec 11 Python
Python数据结构与算法之图的基本实现及迭代器实例详解
Dec 12 Python
python opencv旋转图像(保持图像不被裁减)
Jul 26 Python
Django网络框架之HelloDjango项目创建教程
Jun 06 Python
python实现函数极小值
Jul 10 Python
pytorch获取vgg16-feature层输出的例子
Aug 20 Python
python 字符串常用函数详解
Sep 11 Python
python实现两个一维列表合并成一个二维列表
Dec 02 Python
基于python 等频分箱qcut问题的解决
Mar 03 Python
Python是什么 Python的用处
May 26 Python
python利用opencv保存、播放视频
Nov 02 Python
Python实现文字pdf转换图片pdf效果
Apr 03 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
解决dede生成静态页和动态页转换的一些问题,及火车采集入库生成动态的办法
2007/03/29 PHP
php中防止伪造跨站请求的小招式
2011/09/02 PHP
linux命令之调试工具strace的深入分析
2013/06/03 PHP
php字符串替换函数substr_replace()用法实例
2015/03/17 PHP
js 操作select相关方法函数
2009/12/06 Javascript
ASP小贴士/ASP Tips javascript tips可以当桌面
2009/12/10 Javascript
基于jquery插件制作左右按钮与标题文字图片切换效果
2013/11/07 Javascript
同域jQuery(跨)iframe操作DOM(示例代码)
2013/12/13 Javascript
js截取中英文字符串、标点符号无乱码示例解读
2014/04/17 Javascript
javascript 回调函数详解
2014/11/11 Javascript
JS+CSS实现弹出全屏灰黑色透明遮罩效果的方法
2014/12/20 Javascript
Bootstrap编写一个兼容主流浏览器的受众门户式风格页面
2016/07/01 Javascript
jQuery使用Layer弹出层插件闪退问题
2016/12/22 Javascript
JS动画定时器知识总结
2018/03/23 Javascript
微信小程序倒计时功能实例代码
2018/07/17 Javascript
一步一步的了解webpack4的splitChunk插件(小结)
2018/09/17 Javascript
ES5新增数组的实现方法
2020/05/12 Javascript
JavaScript/TypeScript 实现并发请求控制的示例代码
2021/01/18 Javascript
Webpack3+React16代码分割的实现
2021/03/03 Javascript
在Python中使用成员运算符的示例
2015/05/13 Python
python读取图片并修改格式与大小的方法
2018/07/24 Python
实例讲解python中的协程
2018/10/08 Python
Python3.4 splinter(模拟填写表单)使用方法
2018/10/13 Python
Python学习笔记之列表推导式实例分析
2019/08/13 Python
python GUI库图形界面开发之PyQt5窗口控件QWidget详细使用方法
2020/02/26 Python
Python如何批量获取文件夹的大小并保存
2020/03/31 Python
微信html5页面调用第三方位置导航的示例
2018/03/14 HTML / CSS
去加拿大的旅行和假期:Canadian Affair
2016/10/25 全球购物
办理居住证介绍信
2014/01/15 职场文书
吃空饷专项治理工作实施方案
2014/03/04 职场文书
数字化校园建设方案
2014/05/03 职场文书
竞聘上岗演讲
2014/05/19 职场文书
工程售后服务承诺书
2014/05/21 职场文书
党员评议思想汇报
2014/10/08 职场文书
个人简历求职信范文
2015/03/20 职场文书
导游词之南京栖霞山
2019/10/18 职场文书