导航

 

服务器接收文件时,有时会使用表单接收的方式,这意味着我们需要使用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)命令,效果更佳

posted on 2019-01-08 11:28  slqt  阅读(17512)  评论(0编辑  收藏  举报