利用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中使用sys模板和logging模块获取行号和函数名的方法
Apr 15 Python
python中lambda函数 list comprehension 和 zip函数使用指南
Sep 28 Python
Python Tkinter GUI编程入门介绍
Mar 10 Python
浅谈Python 集合(set)类型的操作——并交差
Jun 30 Python
Python selenium 三种等待方式解读
Sep 15 Python
windows下安装python的C扩展编译环境(解决Unable to find vcvarsall.bat)
Feb 21 Python
Python将一个CSV文件里的数据追加到另一个CSV文件的方法
Jul 04 Python
Python 变量类型详解
Oct 10 Python
Python3.8中使用f-strings调试
May 22 Python
Django查询优化及ajax编码格式原理解析
Mar 25 Python
解决pytorch 交叉熵损失输出为负数的问题
Jul 07 Python
Matplotlib中%matplotlib inline如何使用
Jul 28 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下操作Linux消息队列完成进程间通信的方法
2010/07/24 PHP
PHP函数之error_reporting(E_ALL ^ E_NOTICE)详细说明
2011/07/01 PHP
PHP中数组的三种排序方法分享
2012/05/07 PHP
php给每个段落添加空格的方法
2015/03/20 PHP
php的闭包(Closure)匿名函数初探
2016/02/14 PHP
thinkPHP中验证码的简单实现方法
2016/12/05 PHP
Discuz不使用插件实现简单的打赏功能
2019/03/21 PHP
用jquery来定位
2007/02/20 Javascript
使用正则替换变量
2007/05/05 Javascript
javascript replace方法与正则表达式
2008/02/19 Javascript
jQuery EasyUI API 中文文档 - Parser 解析器
2011/09/29 Javascript
Jquery Uploadify多文件上传带进度条且传递自己的参数
2013/08/28 Javascript
javascript实现图片上传前台页面
2015/08/18 Javascript
个人网站留言页面(前端jQuery编写、后台php读写MySQL)
2016/05/03 Javascript
基于JavaScript实现本地图片预览
2017/02/08 Javascript
javascript 秒表计时器实现代码
2017/03/09 Javascript
利用vueJs实现图片轮播实例代码
2017/06/03 Javascript
vue绑定class与行间样式style详解
2017/08/16 Javascript
JS实现标签滚动切换效果
2017/12/25 Javascript
解决layer弹层遮罩挡住窗体的问题
2018/08/17 Javascript
es6数据变更同步到视图层的方法
2019/03/04 Javascript
js之切换全屏和退出全屏实现代码实例
2019/09/09 Javascript
vue项目里面引用svg文件并给svg里面的元素赋值
2020/08/17 Javascript
JavaScript实现跟随鼠标移动的盒子
2021/01/28 Javascript
vue-router路由懒加载及实现的3种方式
2021/02/28 Vue.js
Python中实现字符串类型与字典类型相互转换的方法
2014/08/18 Python
Python中input和raw_input的一点区别
2014/10/21 Python
在Python3中初学者应会的一些基本的提升效率的小技巧
2015/03/31 Python
Python爬取网易云音乐热门评论
2017/03/31 Python
python归并排序算法过程实例讲解
2020/11/04 Python
学生会个人自荐书范文
2014/02/12 职场文书
机械电子工程专业自荐书
2014/06/10 职场文书
欢度春节标语
2014/07/01 职场文书
优秀党员先进事迹材料
2014/12/18 职场文书
酒店员工管理制度
2015/08/05 职场文书
JavaScript 数组去重详解
2021/09/15 Javascript