파이썬으로 요청과 함께 "multipart / form-data"를 보내는 방법은 무엇입니까?


211

multipart/form-data파이썬으로 요청 을 보내는 방법은 무엇입니까? 파일을 보내는 방법은 이해하지만이 방법으로 양식 데이터를 보내는 방법은 이해할 수 없습니다.


귀하의 질문은 분명하지 않습니다. 무엇을 달성하고 싶습니까? 양식에 파일을 업로드하지 않고 "multipart / form-data"를 보내시겠습니까?
한스 다음

4
files매개 변수가 두 가지를 수행하는 데 사용 된다는 사실 은 매우 나쁜 API입니다. 멀티 파트 데이터 전송 이라는 제목의 문제가 발생 했습니다.이를 해결 하려면 더 나은 API가 필요 합니다. files다중 부품 데이터를 전송하기 위해 매개 변수를 사용 하는 것이 최선의 방법으로 잘못되었다는 데 동의 하면 위 문제에서 API를 변경하도록 요청하십시오.
Piotr Dobrogost

@PiotrDobrogost 그 문제는 종료되었습니다. 사람들이 관련 문제 또는 기타 비공개 문제에 대해 언급하도록 권장하지 마십시오.
Ian Stapleton Cordasco

1
당신의 의견이 닫히기 전에 게시 된 것을 깨달았습니다. StackOverflow가 시간 순서대로 항목을 유지하지 않는 방법이 싫습니다.
Ian Stapleton Cordasco

답변:


166

기본적으로 files매개 변수 (사전) 를 지정 requests하면 multipart/form-dataPOST 대신 POST를 보냅니다 application/x-www-form-urlencoded. 그러나 해당 사전에서 실제 파일을 사용하는 것으로 제한되지는 않습니다.

>>> 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, 그런 다음에 따라 dataPOST가 몸을 만드는 데 사용됩니다 무엇. 경우 data문자열 인 만이 사용할 수 잡아 줄께; 그렇지 모두 datafiles의 요소와 함께 사용되는 data제 나열.

고급 멀티 파트 지원requests-toolbelt 을 포함 하는 우수한 프로젝트 도 있습니다 . 매개 변수 와 동일한 형식으로 필드 정의를 사용 하지만, 달리 기본적으로 파일 이름 매개 변수를 설정하지 않습니다. 또한 열린 파일 객체에서 요청을 스트리밍 할 수 있습니다. 여기서 먼저 요청 본문을 메모리에 구성합니다.filesrequestsrequests

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}
)

필드는 동일한 규칙을 따릅니다. 파일 이름, 부분 MIME 형식 또는 추가 헤더를 추가하려면 2 ~ 4 개의 요소가있는 튜플을 사용하십시오. files매개 변수 와 달리 filename튜플을 사용하지 않으면 기본값을 찾으려고 하지 않습니다.


3
files = {}를 사용하면 headers = { 'Content-Type': 'blah blah'}를 사용해서는 안됩니다!
zaki

5
@ zaki : 실제로 multipart/form-dataContent-Type 에는 포스트 본문의 부분을 제거하는 데 사용되는 경계 값이 포함 되어야 하기 때문입니다. , 설정하지 Content-Type헤더 보장하지만 requests올바른 값으로 설정합니다.
Martijn Pieters

중요 사항 : 요청 multipart/form-data의 값 files=이 진실 인 것처럼 요청이 전송 되므로 multipart/form-data요청 을 보내야 하지만 파일을 포함하지 않는 경우와 같이 진실하지만 의미없는 값을 {'':''}설정 data=하고 요청 본문으로 설정할 수 있습니다. 이 작업을 수행하는 경우 Content-Type헤더를 직접 제공하지 마십시오 . requests당신을 위해 그것을 설정합니다. 여기 진실 체크를 볼 수 있습니다 github.com/psf/requests/blob/...
다니엘 Situnayake

@DanielSitunayake 같은 해킹이 필요하지 않습니다. filesdict 에 모든 필드를 넣으면 파일 일 필요는 없습니다 (튜플 형식을 사용하고 파일 이름을로 설정하십시오 None). 더 좋은 방법은 requests_toolbelt프로젝트를 사용하는 것 입니다.
Martijn Pieters

@MartijnPieters 덕분에 튜플 형식의 트릭이 훌륭합니다! 시도해 볼 것입니다.
다니엘 시투 나야 케

107

이전 답변이 작성되었으므로 요청이 변경되었습니다. 자세한 내용 은 Github버그 스레드 를 확인하고 예제는 이 주석 을 참조하십시오.

요컨대, files 매개 변수는 요청의 멀티dict 파트 인코딩 파일 POST 섹션에 설명 된대로 키가 양식 필드의 이름이고 값이 문자열 또는 2, 3 또는 4 길이 튜플 인 a를 사용 합니다. 빠른 시작:

>>> 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

2
당신이 구별해야하는 경우 namefilename뿐만 아니라 같은 이름의 여러 필드가?
Michael

1
@Michael과 비슷한 문제가 있습니다. 질문을보고 무언가를 제안 할 수 있습니까? [link] ( stackoverflow.com/questions/30683352/… )
Shaardool 2016 년

누군가 같은 이름의 여러 필드를 사용 하여이 문제를 해결 했습니까?
user3131037

1
빈 문자열을 files튜플 의 첫 번째 값으로 전달하는 트릭은 더 이상 작동하지 않습니다. requests.post data추가 비 파일 multipart/form-data매개 변수 를 보내려면 대신 매개 변수 를 사용해야 합니다.
Lucas Cimon

1
전달 None빈 문자열 대신하여 작동하는 것 같다
알렉상드르 BLIN

73

당신은 사용할 필요가 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 a2-tuple, .3-tupleor a4-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는 당신을위한 파이썬 정도가 아닌 경우, 사용을 고려 요청이 툴 벨트 ( pip install requests_toolbelt의 확장 인) 의 핵심 요구는 그 파일 업로드가뿐만 아니라 스트리밍에 대한 지원을 제공하는 모듈 MultipartEncoder 대신 사용할 수 있습니다 files, 어느도 있습니다 페이로드를 사전, 튜플 또는 목록으로 정의합니다.

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'),
           )
    )

감사합니다. 열쇠의 순서는 나에게 중요했고 이것은 많은 도움이되었습니다.
Splendor

놀랄 만한. 설명 할 수없는 일로, 동일한 키에 대해 두 가지 다른 값이 필요합니다. 이것은 놀랍다. 감사합니다.
ajon

@ ccpizza, 실제로이 줄의 의미는 무엇입니까? > "( 'file.py', open ( 'file.py', 'rb'), 'text / plain')" 그것은 나를 위해 작동하지 않습니다 :(
Denis Koreyba

@DenisKoreyba : 이것은 파일 업로드 필드의 예이며, 이름 file.py이 지정된 파일 이 스크립트와 동일한 폴더에 있다고 가정합니다 .
ccpizza

1
None빈 문자열 대신 사용할 수 있습니다 . 그러면 요청에 파일 이름이 전혀 포함되지 않습니다. 그래서 대신 Content-Disposition: form-data; name="action"; filename=""이 될 것입니다 Content-Disposition: form-data; name="action". 서버에서 해당 필드를 파일이 아닌 양식 필드로 승인하는 것이 중요했습니다.
Mitar

8

다음은 요청을 사용하여 추가 매개 변수가있는 단일 파일을 업로드하는 간단한 코드 스 니펫입니다.

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)

콘텐츠 유형을 명시 적으로 지정할 필요는 없습니다.

참고 : 위의 답변 중 하나에 대해 언급하고 싶었지만 평판이 낮아서 여기에 새로운 답변을 작성하지 못했습니다.


4

name사이트의 HTML에있는 업로드 파일 의 속성 을 사용해야합니다 . 예:

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)

그리고, 파일이 성공적으로 업로드되었습니다


3

멀티 파트 / 폼 데이터 키 및 값 보내기

컬 명령 :

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

파이썬 요청-더 복잡한 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

파이썬 요청-멀티 파트 인코딩 파일 POST :

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

그게 다야.


-1

다음은 하나의 큰 단일 파일을 멀티 파트 formdata로 업로드하는 데 필요한 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)

서버 측의 경우 https://github.com/expressjs/multer 에서 multer 문서를 확인 하십시오. 여기에서 single ( 'fieldName') 필드는 다음과 같이 하나의 단일 파일을 허용하는 데 사용됩니다.

var upload = multer().single('fieldName');
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.