服務器接收文件時,有時會使用表單接收的方式,這意味着我們需要使用Python的requests上傳表單數據和文件。
常用的方式一般如下:
data = {
'name': 'nginx'
}
files = {'file': open("abc.csv", 'rb')}
response = requests.post(url, data=data, files=files)
files是封裝好的參數,直接包括了文件內容,文件名,格式等,data則是表單內容,但這樣做有一個問題,文件是中文名時,requests就會報錯,哪怕encode轉碼成utf8也沒用
百度發現除了requests的這個方法,還可以用一個第三方包MultipartEncoder,而這個包相對來說比較靈活。
一般是from requests_toolbelt.multipart.encoder import MultipartEncoder,這樣導入使用
由於公司項目需要兼容各種環境,不主張使用大量第三方庫,我精簡模塊后提取出my_compat 文件,變成 from my_compat import MultipartEncoder這樣導入使用
但MultipartEncoder也存在無法轉化中文名的問題,所以我在代碼里取了巧,先把文件名轉化成可解析的字符,然后用to_string方法解析,最后把解析后的字符串轉化回去
from requests_toolbelt.multipart.encoder import MultipartEncoder
from my_compat import MultipartEncoder
import urllib
import requests
import json
encoded_name = urllib.quote(file_name.encode('utf-8'))//取巧做法,先轉化字符
with open(res_path, 'rb') as f_:
m = MultipartEncoder(
fields={'file': (encoded_name, f_,
'application/octet-stream')}
)
decoded_m = m.to_string()//解析時不支持中文
decoded_m = decoded_m.replace(encoded_name, file_name)//替代轉化
response = requests.post(url,
data=decoded_m,
headers={'Content-Type': m.content_type,
'charset': 'UTF-8'},
verify=False)
try:
content = json.loads(response.content)
except ValueError:
content = response.content
return content, response.status_code
后來發現這樣做其實很不方便,所以我就閱讀MultipartEncoder的源碼,發現content_type其實就是一個很簡單的隨機字符串的構造,而數據的字符流只要符合一定規范就可以構造,再結合requests包,寫出了如下的代碼,
#coding=utf8
import requests
from uuid import uuid4
import os
file_name='test'
url=
boundary=uuid4().hex
header={'Content-Type': 'multipart/form-data; boundary={0}'.format(boundary),'charset': 'UTF-8'}
with open(r'C:\test'.decode('utf8'), 'r') as f:
content=f.readlines()
print content
content=''.join(content)
datas = '--{0}{1}Content-Disposition: form-data; name="file"; filename="{2}"{1}Content-Type: application/octet-stream{1}{1}{3}{1}--{0}--{1}'. \
format(boundary,os.linesep, file_name, content,boundary)
print repr(datas)
print header
response = requests.post(url,
data=datas,
headers=header,
verify=False)
print response.status_code,response.text
在windows上調試可以,但在linux上調試一直報錯,后來把os.linesep換成指定的'\r\n'分隔符就可以成功了,不知道是我們公司服務器設置問題還是這個庫的解析問題。
#coding=utf8
import requests
from uuid import uuid4
import os
file_name='test'
url=
boundary=uuid4().hex
header={'Content-Type': 'multipart/form-data; boundary={0}'.format(boundary),'charset': 'UTF-8'}
with open(r'C:\test'.decode('utf8'), 'r') as f:
content=f.readlines()
print content
content=''.join(content)
datas = '--{0}{1}Content-Disposition: form-data; name="file"; filename="{2}"{1}Content-Type: application/octet-stream{1}{1}{3}{1}--{0}--{1}'. \
format(boundary,'\r\n', file_name, content,boundary)
print repr(datas)
print header
response = requests.post(url,
data=datas,
headers=header,
verify=False)
print response.status_code,response.text
結合saltstack,在proxy上執行的 "salt '{}' cp.push {}".format(path, agent_id, file_path)命令,效果更佳
