解決python發送multipart/form-data請求上傳文件的問題


服務器接收文件時,有時會使用表單接收的方式,這意味着我們需要使用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)命令,效果更佳


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM