requests请求接口---上传文件
前言
接口测试中,上传文件的测试场景非常常见。例如:上传头像(图片)、上传文件、上传视频等。
下面以一个上传图片的例子为大家讲解如何通过 python 测试上传文件接口。
①首先通过抓包分析上传文件接口的请求参数:
②下面是上传文件接口脚本。
把目标文件以open打开,然后存储到变量file。并且使用 files参数指明请求的参数名称、上传文件的类型、以及上传文件的路径。
注意
content-type参数,如果我们通过form-data的方式上传文件,我们发送post请求的时候,headers这个参数中一定不能要包括这个值;
即不需要包括 {"Content-Type":"multipart/form-data"} ,例如:
requests库会自动添加这个元素 {"Content-Type":"multipart/form-data"} ;加了反而会报错,从而导致请求不成功。
在前端允许和支持上传的所有格式文件中,当选择不同格式文件时对接口发起请求时,Form Data会自动识别文件的Content-Type【根据传入的文件格式自动解析为headers中的Content-Type】以及Content-Disposition【该字段中包括接口的传参类型(例如下图的form-data),传入文件名(例如下图的filename)等】。
例如:
示例
requests请求接口上传文件代码示例:
import requests from utils.LogUtil import my_log from faker import Faker log=my_log() faker = Faker('zh_CN') class marketCreate(): def loginToken(self,mobile,code): ''' 登陆、注册接口 :param mobile: 手机号 :param code: 验证码 :return: ''' url='http://api.xxxxxx.net/v1/user/loginOrRegister' #登陆接口请求地址 headers={'Content-Type': 'application/json'} #请求头信息,json数据类型 json={"code":code,"deviceId":"5de548ad0e268bc0","deviceName":"HWNXT","mobile":mobile,"mobilePrefix":"63","version":"39"} #请求数据 r=requests.post(url,headers=headers,json=json) #发送请求 token=r.json()['data']['userVO']['token'] #获取token log.info('手机号'+mobile+'的token值为:'+token) #打印日志 return token #将token返回出去 def uploadimages(self,mobile,file_path,filename): ''' 上传图片接口 :param mobile: 手机号 :param file_path: 文件路径 :param filename: 文件名称 :return: ''' token = self.loginToken(mobile=mobile, code='789789') #请求登陆接口,获取token url='http://api.xxxxxx.net/v1/upload/images' #上传文件接口请求地址 headers={'token': token} #请求头信息关联token。不需要赋值Content-Type,requests库会帮忙添加这个元素,加了可能会报错。 file_data={'file':(filename,open(file_path,'rb'),'image/jpeg')} #重点:上传文件请求数据。 r=requests.post(url=url,headers=headers,files=file_data) #发送请求 print(r.json()) #打印响应结果 if __name__ == '__main__': m=marketCreate() # 测试,输入手机号、文件地址、文件名称信息; m.uploadimages(mobile='639266558329',file_path='D:pyCharm_workcmkjProjectdatamarket.jpg',filename='123456.jpg')
执行结果:
C:Python38python.exe D:/pyCharm_work/cmkjProject/page/marketPage.py 2020-01-02 13:48:09,580-D:pyCharm_workcmkjProjectutilsLogUtil.py-INFO-手机号639266558329的token值为:ba9b4f622fa4d6461523870c0d00df46 {'code': 0, 'data': '/group1/M00/00/00/rBDKPF4NhE-ABIFmAAGhT9tm-NA158.jpg', 'success': True} Process finished with exit code 0
总结:
如果需要发送文件到服务器,比如上传图片、视频等,就需要发送二进制数据。
一般上传文件接口的请求头中定义传入参数数据的媒体类型使用的都是
Content-Type: multipart/form-data;
数据类型,可以发送文件,也可以发送相关的消息体数据。
POST一个多部分编码(Multipart-Encoded)的文件
使用 requests 上传文件的基本步骤
- 构造文件数据,通过 open 函数以二进制方式打开文件
- 构造相关数据
- 发送请求,将文件数据以files参数传入,其他消息体数据通过data、json、headers、cookies传入
url = 'http://httpbin.org/post' # 上传文件接口 files = { 'file': ('test.png', # 文件名称 open('../file/test.png', 'rb'), # 文件路径 'image/png', # 文件类型 {'Expires': '0'} # 其他参数,非必传 ) } # => 打开上传文件并且加入文件相关参数 data = { "name": "test" } # data传入请求参数dict,files传入待上传文件参数dict r = requests.post(url, data=data, files=files) print(r.json())
注意
files参数字典里的file键名称是根据上传组件的name属性来改变的,不一定是file。【抓包时可以通过请求体中的 binary行的参数名获取name属性】
如下图上传组件,当你上传一张图片时,抓包可以发现会传两个值,一个是fileField,一个是type,所以你的文件数据dict要包含 ‘fileField’和‘type’两个key
files = { 'fileField': ('test.png', # 文件名称 open('../file/test.png', 'rb'), # 文件路径 'image/png', # 文件类型 {'Expires': '0'} # 其他参数,非必传 ), 'type': 1 } # => 打开上传文件并且加入文件相关参数
Python使用requests库发送上传zip类型文件的post请求
第一步,通过chrome浏览器的开发者工具,获得发送的参数。
第二步,编写python代码
使用request库的post方法。注意的是要添加files参数,例如:
files ={'app_filename':open('portal-1.0-SNAPSHOT-fat.jar.zip','rb')}
zip压缩包的content-type是application/x-zip-compressed,其他的文件的content-type是application/octet-stream。
其中:
① app_filename是F12工具里抓出来的from data里的标有{binary}这一行的参数名【可以理解为name属性】。
② portal-1.0-SNAPSHOT-fat.jar.zip是本地电脑zip文件名。(需要请求接口的文件)
③ rb是读取二进制文件。因为form data媒体类型表示文件是以二进制形式上传。
④其余的常规参数,放到data参数里。例如上图的 image_name:fff就是常规参数。
⑤在接口请求的请求头里注意添加cookies或者 Authorization,(这里测试的网站用的是Authorization验证用户身份。如果没有该参数或者该参数错误或过期,会返回401)。
⑥完整代码如下:
path = os.path.split(os.path.realpath(__file__))[0]
url = host + '/dashboard/cicd/images'
headers = { 'Authorization':'6bae7b70-8dae-4f74-9631-680b9501b52', 'cookie': "_ga=GA1.3.733851079.1534745675; Hm_lvt_dde6ba2851f3db0ddc415ce0f895822e=1537859803; _ga=GA1.3.733851079.1534745675; Hm_lvt_dde6ba2851f3db0ddc415ce0f895822e=1537859803", }
datat = {'image_name': 'abcd', 'image_description': 'ccccvcc', 'image_label': '1cc1fcc', 'basic_image': 'openjdk:10', 'store_path': '/opt/app/lzw/'}
files = {'app_filename': ( 'portal-1.0-SNAPSHOT-fat.jar.zip', open(os.path.join(path, 'portal-1.0-SNAPSHOT-fat.jar.zip'), 'rb'), 'application/x-zip-compressed')}
# files ={'app_filename':open('portal-1.0-SNAPSHOT-fat.jar.zip','rb')} 和上面的功能一样 result = requests.post(url, files=files, data=datat, headers=headers)
r1 = result.text print(result.text)
Python使用requests库发送上传excel文件的post请求
【注意】
zip压缩包的 content-type 是 application/x-zip-compressed ;
其他的文件的 content-type 是 application/octet-stream 。如上图二【直接点击(view source)】(图一是点击(view parsed即解析后的表单内容)
添加files参数:
files = {'files': (file_name, open(get_file_path(filename=file_name), 'rb'))}
① files是F12工具里抓出来的from data表单数据请求体里的标有 binary 这一行的参数名【可以理解为name属性】。【如图一中的粗红线框出来的文件参数】
② file_name 对应需要上传文件的文件名。
③ open 函数是为了获取需要上传的文件的流。
④ rb是读二进制文件。因为请求头中的 content_type字段标识 form data 数据媒体类型即表示文件是以二进制形式上传。
⑤其余的常规参数,放到data参数里。【如上图中的细红线框出的常规参数,即作为同文件上传时还有需要一起上传的body体数据】
代码如下:
def clue_add(): file_name = 'create_club.xls'
files = {'files': (file_name, open(get_file_path(filename=file_name), 'rb'))}
url_upload = 'http://gasst-uat.test.geely.com/cafe.portal-service/frame_center/common/attachmentChunkUpload'
headers = {'Cookie': ReadConfig().read_config('project_GHelper', 'token')}
data_upload = { 'fileName': 'create_club.xls', 'chunkCount': 1, 'chunkIndex': 1 }
r_upload = requests.post(url=url_upload, files=files, headers=headers, data=data_upload)