利用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单例模式实例分析
Jan 14 Python
Python抓取淘宝下拉框关键词的方法
Jul 08 Python
深入解析Python中的上下文管理器
Jun 28 Python
Django的分页器实例(paginator)
Dec 01 Python
python斐波那契数列的计算方法
Sep 27 Python
PyQt弹出式对话框的常用方法及标准按钮类型
Feb 27 Python
详解python做UI界面的方法
Feb 27 Python
树莓派用python中的OpenCV输出USB摄像头画面
Jun 22 Python
解决python 上传图片限制格式问题
Oct 30 Python
Python列表list操作相关知识小结
Jan 29 Python
DRF使用simple JWT身份验证的实现
Jan 14 Python
python如何做代码性能分析
Apr 26 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
追忆往昔!浅谈收音机的百年发展历史
2021/03/01 无线电
我的论坛源代码(三)
2006/10/09 PHP
php下连接ftp实现文件的上传、下载、删除文件实例代码
2010/06/03 PHP
php readfile()修改文件上传大小设置
2017/08/11 PHP
PHP实现的各类hash算法长度及性能测试实例
2017/08/27 PHP
PHP数组array类常见操作示例
2020/05/15 PHP
document.documentElement && document.documentElement.scrollTop
2007/12/01 Javascript
js点击事件链接的问题解决
2014/04/25 Javascript
基于Jquery实现万圣节快乐特效
2015/11/01 Javascript
详解JavaScript正则表达式之RegExp对象
2015/12/13 Javascript
jQuery EasyUI框架中的Datagrid数据表格组件结构详解
2016/06/09 Javascript
JS实现n秒后自动跳转的两种方法
2020/11/30 Javascript
javascript 中的try catch应用总结
2017/04/01 Javascript
详解通过JSON数据使用VUE.JS
2017/05/26 Javascript
关于JavaScript的单双引号嵌套问题
2017/08/20 Javascript
深入解析ES6中的promise
2018/11/08 Javascript
微信小程序下拉框搜索功能的实现方法
2019/07/31 Javascript
JavaScript数组排序的六种常见算法总结
2020/08/18 Javascript
用生成器来改写直接返回列表的函数方法
2017/05/25 Python
python基础知识(一)变量与简单数据类型详解
2019/04/17 Python
python读写csv文件方法详细总结
2019/07/05 Python
Python3实现监控新型冠状病毒肺炎疫情的示例代码
2020/02/13 Python
Python实现Wordcloud生成词云图的示例
2020/03/30 Python
Python批量安装卸载1000个apk的方法
2020/04/10 Python
Django serializer优化类视图的实现示例
2020/07/16 Python
西班牙著名的珠宝首饰品牌:P D PAOLA
2018/09/15 全球购物
PHP引擎php.ini参数优化深入讲解
2021/03/24 PHP
生态学毕业生自荐信
2013/10/27 职场文书
求职自荐信
2013/12/14 职场文书
简历自我评价怎么写好呢?
2014/01/04 职场文书
cf搞笑广告词
2014/03/14 职场文书
2015年施工员工作总结范文
2015/04/20 职场文书
关于公司年会的开幕词
2016/03/04 职场文书
2016年党员公开承诺书范文
2016/03/24 职场文书
详解Redis集群搭建的三种方式
2021/05/31 Redis
React-vscode使用jsx语法的问题及解决方法
2021/06/21 Javascript