在开发过程中经常有需要上传文件,而python上传文件官方并不直接支持,网上的大部分方案都是用python poster库来支持文件上传,用这种方式上传文件确实也特别的简单。
poster用法请参考这里:http://atlee.ca/software/poster/
今天我要介绍的是模拟网页来上传文件,这样不需要依赖第三方库,使用和传播都更加的方便。
既然是模拟网页提交,我们先来看看网页上传文件到底上传了什么内容?
这是请求的header,我们主要关注的是Content-Length、Content-Type、和提交的数据,如果上传文件涉及到身份的验证,可能还需要关注Cookie
这是提交的内容,总共提交了四个字段
- app 字符串 值为dlife
- platform 字符串 值为android
- type 字符串 值为package
- file 文件 文件流
可以看到一些规律,字符型数据都是如下的内容
------WebKitFormBoundarywxxHf5sLp9I0dQCs Content-Disposition: form-data; name="app" dlife
总共分为4行
第一行 ——WebKitFormBoundarywxxHf5sLp9I0dQCs boundary值,和Content-Type里面boundary一致,boundary是内容之间的分隔符
第二行 Content-Disposition: form-data; name=”app” 数据类型和字段名称
第三行 为空(\r\n)
第四行 dlife 是字段app的值
如果内容是文件的话还多了一行Content-Type: application/octet-stream ,就是文件类型,比如android apk文件类型是application/vnd.android.package-archive,这个可以用python mimetypes.guess_type(file)方法获取文件类型
代码如下:
组合请求参数
def get_request_param(params, boundary):
"""
获取请求的参数
args:
params: 请求的参数
boundary: 上传的boundary
return:
string: 请求参数
"""
body = ''
if len(params) > 0:
CRLF = '\r\n'
for key in params.keys():
value = params[key]
body += '--' + boundary + CRLF
if os.path.exists(value):
file_type = mimetypes.guess_type(value)[0]
file_name = os.path.basename(value)
if file_type is None:
file_type = "text/plain; charset=utf-8"
body += 'Content-Disposition: form-data; name="' \
+ key + '"; filename="' + file_name + '"' + CRLF
body += "Content-Type: " + file_type + CRLF
body += CRLF
body += open(value, 'rb').read() + CRLF
else:
body += 'Content-Disposition: form-data; name="' + key + '"' + CRLF
body += CRLF
body += value + CRLF
body += "--" + boundary + "--" + CRLF
return body
上传文件
import urllib
import urllib2
import cookielib
import json
import sys
import os
import uuid
import mimetypes
upload_url = host + 'admin/app-package'
boundary = '--------ksc' + uuid.uuid4().hex
param = {
'file': apk_url,
'app': app,
'platform': platform,
'type': 'package'
}
request_data = get_request_param(param, boundary)
req = urllib2.Request(upload_url)
req.add_header("User-Agent", 'ksc')
req.add_header("Content-Type", "multipart/form-data, boundary=" + boundary)
res = urllib2.urlopen(req, request_data)
content = res.read().decode('utf-8')