簡體   English   中英

如何在 python 中發送帶有請求的“multipart/form-data”?

[英]How to send a "multipart/form-data" with requests in python?

如何在 python 中發送帶有requestsmultipart/form-data 如何發送文件,我明白,但是如何通過這種方法發送表單數據就無法理解。

基本上,如果您指定files參數(字典),則requests將發送multipart/form-data POST 而不是application/x-www-form-urlencoded POST。 您不僅限於使用該字典中的實際文件,但是:

>>> import requests
>>> response = requests.post('http://httpbin.org/post', files=dict(foo='bar'))
>>> response.status_code
200

和 httpbin.org 讓你知道你發布的標題; response.json()我們有:

>>> from pprint import pprint
>>> pprint(response.json()['headers'])
{'Accept': '*/*',
 'Accept-Encoding': 'gzip, deflate',
 'Connection': 'close',
 'Content-Length': '141',
 'Content-Type': 'multipart/form-data; '
                 'boundary=c7cbfdd911b4e720f1dd8f479c50bc7f',
 'Host': 'httpbin.org',
 'User-Agent': 'python-requests/2.21.0'}

更好的是,您可以通過使用元組而不是單個字符串或字節對象來進一步控制每個部分的文件名、內容類型和其他標題。 元組預計包含 2 到 4 個元素; 文件名、內容、可選的內容類型和可選的其他標題字典。

我將使用帶有None作為文件名的元組形式,以便從這些部分的請求中刪除filename="..."參數:

>>> files = {'foo': 'bar'}
>>> print(requests.Request('POST', 'http://httpbin.org/post', files=files).prepare().body.decode('utf8'))
--bb3f05a247b43eede27a124ef8b968c5
Content-Disposition: form-data; name="foo"; filename="foo"

bar
--bb3f05a247b43eede27a124ef8b968c5--
>>> files = {'foo': (None, 'bar')}
>>> print(requests.Request('POST', 'http://httpbin.org/post', files=files).prepare().body.decode('utf8'))
--d5ca8c90a869c5ae31f70fa3ddb23c76
Content-Disposition: form-data; name="foo"

bar
--d5ca8c90a869c5ae31f70fa3ddb23c76--

files也可以是一個二值元組列表,如果您需要排序和/或多個具有相同名稱的字段:

requests.post(
    'http://requestb.in/xucj9exu',
    files=(
        ('foo', (None, 'bar')),
        ('foo', (None, 'baz')),
        ('spam', (None, 'eggs')),
    )
)

如果同時指定filesdata ,則取決於用於創建 POST 正文的data 如果data是字符串,則只使用它; 否則將同時使用datafiles ,首先列出data的元素。

還有優秀的requests-toolbelt項目,其中包括 高級多部分支持 它采用與files參數格式相同的字段定義,但與requests不同,它默認不設置文件名參數。 此外,它可以從打開的文件對象流式傳輸請求,其中requests將首先在內存中構造請求體:

from requests_toolbelt.multipart.encoder import MultipartEncoder

mp_encoder = MultipartEncoder(
    fields={
        'foo': 'bar',
        # plain file object, no filename or mime type produces a
        # Content-Disposition header with just the part name
        'spam': ('spam.txt', open('spam.txt', 'rb'), 'text/plain'),
    }
)
r = requests.post(
    'http://httpbin.org/post',
    data=mp_encoder,  # The MultipartEncoder is posted as data, don't use files=...!
    # The MultipartEncoder provides the content-type header with the boundary:
    headers={'Content-Type': mp_encoder.content_type}
)

字段遵循相同的約定; 使用包含 2 到 4 個元素的元組來添加文件名、部分 mime 類型或額外的標題。 files參數不同,如果您不使用元組,則不會嘗試查找默認filename值。

自從寫了一些以前的答案以來,請求已更改。 請查看Github上的此問題了解更多詳細信息,並查看此評論作為示例。

簡而言之, files參數采用一個字典,其鍵是表單字段的名稱,值是一個字符串或一個 2、3 或 4 長度的元組,如POST a Multipart-Encoded File 部分所述請求快速入門:

>>> url = 'http://httpbin.org/post'
>>> files = {'file': ('report.xls', open('report.xls', 'rb'), 'application/vnd.ms-excel', {'Expires': '0'})}

在上面,元組的組成如下:

(filename, data, content_type, headers)

如果值只是一個字符串,文件名將與鍵相同,如下所示:

>>> files = {'obvius_session_id': '72c2b6f406cdabd578c5fd7598557c52'}

Content-Disposition: form-data; name="obvius_session_id"; filename="obvius_session_id"
Content-Type: application/octet-stream

72c2b6f406cdabd578c5fd7598557c52

如果值是一個元組並且第一個條目是None文件名屬性將不包括在內:

>>> files = {'obvius_session_id': (None, '72c2b6f406cdabd578c5fd7598557c52')}

Content-Disposition: form-data; name="obvius_session_id"
Content-Type: application/octet-stream

72c2b6f406cdabd578c5fd7598557c52

即使您不需要上傳任何文件,您也需要使用files參數來發送多部分表單 POST 請求。

從原始請求來源:

def request(method, url, **kwargs):
    """Constructs and sends a :class:`Request <Request>`.

    ...
    :param files: (optional) Dictionary of ``'name': file-like-objects``
        (or ``{'name': file-tuple}``) for multipart encoding upload.
        ``file-tuple`` can be a 2-tuple ``('filename', fileobj)``,
        3-tuple ``('filename', fileobj, 'content_type')``
        or a 4-tuple ``('filename', fileobj, 'content_type', custom_headers)``,
        where ``'content-type'`` is a string
        defining the content type of the given file
        and ``custom_headers`` a dict-like object 
        containing additional headers to add for the file.

相關部分是: file-tuple can be a 2-tuple3-tuple or a 4-tuple

基於以上內容,包含要上傳的文件和表單字段的最簡單的多部分表單請求將如下所示:

multipart_form_data = {
    'file2': ('custom_file_name.zip', open('myfile.zip', 'rb')),
    'action': (None, 'store'),
    'path': (None, '/path1')
}

response = requests.post('https://httpbin.org/post', files=multipart_form_data)

print(response.content)

注意None作為純文本字段元組中的第一個參數 - 這是文件名字段的占位符,僅用於文件上傳,但對於文本字段,傳遞None作為第一個參數是必需的,以便數據被提交。

多個同名字段

如果您需要發布多個具有相同名稱的字段,那么您可以將有效負載定義為元組列表(或元組),而不是字典:

multipart_form_data = (
    ('file2', ('custom_file_name.zip', open('myfile.zip', 'rb'))),
    ('action', (None, 'store')),
    ('path', (None, '/path1')),
    ('path', (None, '/path2')),
    ('path', (None, '/path3')),
)

流式請求 API

如果上述 API 對您來說還不夠Pythonic ,那么可以考慮使用請求工具帶( pip install requests_toolbelt ),它是核心請求模塊的擴展,它提供對文件上傳流的支持以及可以用來代替filesMultipartEncoder ,它還允許您將有效負載定義為字典、元組或列表。

MultipartEncoder可用於帶或不帶實際上傳字段的分段請求。 它必須分配給data參數。

import requests
from requests_toolbelt.multipart.encoder import MultipartEncoder

multipart_data = MultipartEncoder(
    fields={
            # a file upload field
            'file': ('file.zip', open('file.zip', 'rb'), 'text/plain')
            # plain text fields
            'field0': 'value0', 
            'field1': 'value1',
           }
    )

response = requests.post('http://httpbin.org/post', data=multipart_data,
                  headers={'Content-Type': multipart_data.content_type})

如果需要發送多個同名字段,或者表單字段的順序很重要,那么可以使用元組或列表代替字典:

multipart_data = MultipartEncoder(
    fields=(
            ('action', 'ingest'), 
            ('item', 'spam'),
            ('item', 'sausage'),
            ('item', 'eggs'),
           )
    )

這是使用請求上傳帶有附加參數的單個文件的簡單代碼片段:

url = 'https://<file_upload_url>'
fp = '/Users/jainik/Desktop/data.csv'

files = {'file': open(fp, 'rb')}
payload = {'file_id': '1234'}

response = requests.put(url, files=files, data=payload, verify=False)

請注意,您不需要明確指定任何內容類型。

注意:想對上述答案之一發表評論,但由於聲譽低而無法評論,因此在此處起草了新回復。

您需要使用站點 HTML 中的上傳文件的name屬性。 例子:

autocomplete="off" name="image">

你看到name="image">嗎? 您可以在用於上傳文件的站點的 HTML 中找到它。 您需要使用它來上傳帶有Multipart/form-data的文件

腳本:

import requests

site = 'https://prnt.sc/upload.php' # the site where you upload the file
filename = 'image.jpg'  # name example

在這里,在圖片的位置,在HTML中添加上傳文件的名稱

up = {'image':(filename, open(filename, 'rb'), "multipart/form-data")}

如果上傳需要點擊上傳按鈕,可以這樣使用:

data = {
     "Button" : "Submit",
}

然后開始請求

request = requests.post(site, files=up, data=data)

大功告成,文件上傳成功

發送 multipart/form-data 鍵和值

卷曲命令:

curl -X PUT http://127.0.0.1:8080/api/xxx ...
-H 'content-type: multipart/form-data; boundary=----xxx' \
-F taskStatus=1

python 請求 - 更復雜的 POST 請求

    updateTaskUrl = "http://127.0.0.1:8080/api/xxx"
    updateInfoDict = {
        "taskStatus": 1,
    }
    resp = requests.put(updateTaskUrl, data=updateInfoDict)

發送多部分/表單數據文件

卷曲命令:

curl -X POST http://127.0.0.1:8080/api/xxx ...
-H 'content-type: multipart/form-data; boundary=----xxx' \
-F file=@/Users/xxx.txt

python 請求 - 發布一個多部分編碼的文件

    filePath = "/Users/xxx.txt"
    fileFp = open(filePath, 'rb')
    fileInfoDict = {
        "file": fileFp,
    }
    resp = requests.post(uploadResultUrl, files=fileInfoDict)

就這樣。

為了澄清上面給出的例子,

“即使您不需要上傳任何文件,您也需要使用 files 參數來發送多部分表單 POST 請求。”

文件={}

不會工作,不幸的是。

您將需要放入一些虛擬值,例如

files={"foo": "bar"}

我在嘗試將文件上傳到 Bitbucket 的 REST API 時遇到了這個問題,並且不得不編寫這個可惡的內容以避免可怕的“不支持的媒體類型”錯誤:

url = "https://my-bitbucket.com/rest/api/latest/projects/FOO/repos/bar/browse/foobar.txt"
payload = {'branch': 'master', 
           'content': 'text that will appear in my file',
           'message': 'uploading directly from python'}
files = {"foo": "bar"}
response = requests.put(url, data=payload, files=files)

:O=

import requests
# assume sending two files
url = "put ur url here"
f1 = open("file 1 path", 'rb')
f2 = open("file 2 path", 'rb')
response = requests.post(url,files={"file1 name": f1, "file2 name":f2})
print(response)

這是將一個大的單個文件作為多部分表單數據上傳所需的python 片段。 在服務器端運行 NodeJs Multer 中間件。

import requests
latest_file = 'path/to/file'
url = "http://httpbin.org/apiToUpload"
files = {'fieldName': open(latest_file, 'rb')}
r = requests.put(url, files=files)

對於服務器端,請查看 multer 文檔: https : //github.com/expressjs/multer這里的字段 single('fieldName') 用於接受單個文件,如下所示:

var upload = multer().single('fieldName');

我正在嘗試使用 python 3 中的請求模塊向 URL_server 發送請求。這對我有用:

# -*- coding: utf-8 *-*
import json, requests

URL_SERVER_TO_POST_DATA = "URL_to_send_POST_request"
HEADERS = {"Content-Type" : "multipart/form-data;"}

def getPointsCC_Function():
  file_data = {
      'var1': (None, "valueOfYourVariable_1"),
      'var2': (None, "valueOfYourVariable_2")
  }

  try:
    resElastic = requests.post(URL_GET_BALANCE, files=file_data)
    res = resElastic.json()
  except Exception as e:
    print(e)

  print (json.dumps(res, indent=4, sort_keys=True))

getPointsCC_Function()

在哪里:

  • URL_SERVER_TO_POST_DATA = 我們要發送數據的服務器
  • HEADERS = 發送的標題
  • file_data = 發送的參數

這是在多部分請求中發送文件的一種方式

import requests
headers = {"Authorization": "Bearer <token>"}
myfile = 'file.txt'
myfile2 = {'file': (myfile, open(myfile, 'rb'),'application/octet-stream')}
url = 'https://example.com/path'
r = requests.post(url, files=myfile2, headers=headers,verify=False)
print(r.content)

其他方法

import requests

url = "https://example.com/path"

payload={}
files=[
  ('file',('file',open('/path/to/file','rb'),'application/octet-stream'))
]
headers = {
  'Authorization': 'Bearer <token>'
}

response = requests.request("POST", url, headers=headers, data=payload, files=files)

print(response.text)

我已經測試了兩者,都可以正常工作。

正如已經指出的那樣,通過在 POST 請求中指定“文件”參數,請求的內容類型設置為multipart/form-data (而不是application/x-www-form-urlencoded ,當僅提交表單數據)。

下面是一個簡單的示例,說明如何在單個請求中上傳具有(可選)相同鍵值的文件列表(即本例中的“文件”)。 您可以選擇包含表單數據參數,如下所示(即 data=payload)。 對於服務器端(如果您需要的話),請查看這個答案,下面的代碼片段是從中獲取的,並且使用了FastAPI

import requests

url = 'http://127.0.0.1:8000/submit'
files = [('files', open('test_files/a.txt', 'rb')), ('files', open('test_files/b.txt', 'rb'))]
payload ={"name": "foo", "point": 0.13, "is_accepted": False}
resp = requests.post(url=url, data=payload, files = files) 
print(resp.json())
import json
import os
import requests
from requests_toolbelt import MultipartEncoder

AUTH_API_ENDPOINT = "http://localhost:3095/api/auth/login"

def file_upload(path_img, token ):
    url = 'http://localhost:3095/api/shopping/product/image'
    name_img = os.path.basename(path_img)

    mp_encoder = MultipartEncoder(
        fields={
            'email': 'mcm9@gmail.com',
            'source': 'tmall',
            'productId': 'product_0001',
            'image': (name_img, open(path_img, 'rb'), 'multipart/form-data')
        #'spam': ('spam.txt', open('spam.txt', 'rb'), 'text/plain'),
        }
    )

    head = {'Authorization': 'Bearer  {}'.format(token),
            'Content-Type': mp_encoder.content_type}

    with requests.Session() as s:
        result = s.post(url, data=mp_encoder, headers=head)

    return result

def do_auth(username, password, url=AUTH_API_ENDPOINT):
    data = {
        "email": username,
        "password": password
    }

    # sending post request and saving response as response object
    r = requests.post(url=url, data=data)

    # extracting response text
    response_text = r.text

    d = json.loads(response_text)
    # print(d)

    return d


if __name__ == '__main__':
    result = do_auth('mcm4@gmail.com','123456')
    token = result.get('data').get('payload').get('token')
    print(token)
    result = file_upload('/home/mcm/Pictures/1234.png',token)
    print(result.json())

郵遞員生成的文件上傳代碼,帶有附加表單字段:

import http.client
import mimetypes
from codecs import encode

conn = http.client.HTTPSConnection("data.XXXX.com")
dataList = []
boundary = 'wL36Yn8afVp8Ag7AmP8qZ0SA4n1v9T'
dataList.append(encode('--' + boundary))
dataList.append(encode('Content-Disposition: form-data; name=batchSize;'))

dataList.append(encode('Content-Type: {}'.format('text/plain')))
dataList.append(encode(''))

dataList.append(encode("1"))
dataList.append(encode('--' + boundary))
dataList.append(encode('Content-Disposition: form-data; name=file; filename={0}'.format('FileName-1.json')))

fileType = mimetypes.guess_type('FileName-1.json')[0] or 'application/octet-stream'
dataList.append(encode('Content-Type: {}'.format(fileType)))
dataList.append(encode(''))

with open('FileName-1.json', 'rb') as f:
  dataList.append(f.read())
dataList.append(encode('--'+boundary+'--'))
dataList.append(encode(''))
body = b'\r\n'.join(dataList)
payload = body
headers = {
  'Cookie': 'XXXXXXXXXXX',
  'Content-type': 'multipart/form-data; boundary={}'.format(boundary)
}
conn.request("POST", "/fileupload/uri/XXXX", payload, headers)
res = conn.getresponse()
data = res.read()
print(data.decode("utf-8"))

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM