Python 스크립트에서 POST를 사용하여 파일 보내기


답변:


214

보낸 사람 : https://requests.readthedocs.io/en/latest/user/quickstart/#post-a-multipart-encoded-file

요청으로 멀티 파트 인코딩 파일을 업로드하는 것이 매우 간단합니다.

with open('report.xls', 'rb') as f:
    r = requests.post('http://httpbin.org/post', files={'report.xls': f})

그게 다야. 나는 농담하지 않습니다-이것은 한 줄의 코드입니다. 파일이 전송되었습니다. 점검 해보자:

>>> r.text
{
  "origin": "179.13.100.4",
  "files": {
    "report.xls": "<censored...binary...data>"
  },
  "form": {},
  "url": "http://httpbin.org/post",
  "args": {},
  "headers": {
    "Content-Length": "3196",
    "Accept-Encoding": "identity, deflate, compress, gzip",
    "Accept": "*/*",
    "User-Agent": "python-requests/0.8.0",
    "Host": "httpbin.org:80",
    "Content-Type": "multipart/form-data; boundary=127.0.0.1.502.21746.1321131593.786.1"
  },
  "data": ""
}

2
파일 크기가 ~ 1.5MB 미만이면 동일한 일을 시도하고 정상적으로 작동합니다. 그렇지 않으면 오류가 발생합니다. 여기보십시오 .
Niks Jain

1
내가하려고하는 것은 성공적으로 완료했지만 지금 로그인 한 후 비디오를 업로드하고 양식을 제출하기 전에 채울 다른 필드가있는 요청을 사용하여 일부 사이트에 로그인하는 것입니다. 동영상 설명, 동영상 제목 등의 값을 어떻게 전달해야합니까
TaraGurung

15
아마도 with open('report.xls', 'rb') as f: r = requests.post('http://httpbin.org/post', files={'report.xls': f})대신 하고 싶을 것이므로 파일을 연 후에 파일을 다시 닫습니다.
Hjulle

3
응? 요청을 보내는 것이 너무 간단 하기 때문에 ?
palsch

1
이 답변은 컨텍스트 관리자를 사용하여 파일이 닫히도록하는 Hjulle의 제안을 포함하도록 업데이트되어야합니다.
bmoran

28

예. 당신은 사용하십시오 urllib2사용하여 모듈 및 인코딩을 multipart/form-data콘텐츠 형식을. 다음은 시작하는 데 도움이되는 샘플 코드입니다. 파일 업로드 일뿐 아니라 그 내용을 읽고 어떻게 작동하는지 확인할 수 있습니다.

user_agent = "image uploader"
default_message = "Image $current of $total"

import logging
import os
from os.path import abspath, isabs, isdir, isfile, join
import random
import string
import sys
import mimetypes
import urllib2
import httplib
import time
import re

def random_string (length):
    return ''.join (random.choice (string.letters) for ii in range (length + 1))

def encode_multipart_data (data, files):
    boundary = random_string (30)

    def get_content_type (filename):
        return mimetypes.guess_type (filename)[0] or 'application/octet-stream'

    def encode_field (field_name):
        return ('--' + boundary,
                'Content-Disposition: form-data; name="%s"' % field_name,
                '', str (data [field_name]))

    def encode_file (field_name):
        filename = files [field_name]
        return ('--' + boundary,
                'Content-Disposition: form-data; name="%s"; filename="%s"' % (field_name, filename),
                'Content-Type: %s' % get_content_type(filename),
                '', open (filename, 'rb').read ())

    lines = []
    for name in data:
        lines.extend (encode_field (name))
    for name in files:
        lines.extend (encode_file (name))
    lines.extend (('--%s--' % boundary, ''))
    body = '\r\n'.join (lines)

    headers = {'content-type': 'multipart/form-data; boundary=' + boundary,
               'content-length': str (len (body))}

    return body, headers

def send_post (url, data, files):
    req = urllib2.Request (url)
    connection = httplib.HTTPConnection (req.get_host ())
    connection.request ('POST', req.get_selector (),
                        *encode_multipart_data (data, files))
    response = connection.getresponse ()
    logging.debug ('response = %s', response.read ())
    logging.debug ('Code: %s %s', response.status, response.reason)

def make_upload_file (server, thread, delay = 15, message = None,
                      username = None, email = None, password = None):

    delay = max (int (delay or '0'), 15)

    def upload_file (path, current, total):
        assert isabs (path)
        assert isfile (path)

        logging.debug ('Uploading %r to %r', path, server)
        message_template = string.Template (message or default_message)

        data = {'MAX_FILE_SIZE': '3145728',
                'sub': '',
                'mode': 'regist',
                'com': message_template.safe_substitute (current = current, total = total),
                'resto': thread,
                'name': username or '',
                'email': email or '',
                'pwd': password or random_string (20),}
        files = {'upfile': path}

        send_post (server, data, files)

        logging.info ('Uploaded %r', path)
        rand_delay = random.randint (delay, delay + 5)
        logging.debug ('Sleeping for %.2f seconds------------------------------\n\n', rand_delay)
        time.sleep (rand_delay)

    return upload_file

def upload_directory (path, upload_file):
    assert isabs (path)
    assert isdir (path)

    matching_filenames = []
    file_matcher = re.compile (r'\.(?:jpe?g|gif|png)$', re.IGNORECASE)

    for dirpath, dirnames, filenames in os.walk (path):
        for name in filenames:
            file_path = join (dirpath, name)
            logging.debug ('Testing file_path %r', file_path)
            if file_matcher.search (file_path):
                matching_filenames.append (file_path)
            else:
                logging.info ('Ignoring non-image file %r', path)

    total_count = len (matching_filenames)
    for index, file_path in enumerate (matching_filenames):
        upload_file (file_path, index + 1, total_count)

def run_upload (options, paths):
    upload_file = make_upload_file (**options)

    for arg in paths:
        path = abspath (arg)
        if isdir (path):
            upload_directory (path, upload_file)
        elif isfile (path):
            upload_file (path)
        else:
            logging.error ('No such path: %r' % path)

    logging.info ('Done!')

1
python 2.6.6에서 Windows 에서이 코드를 사용하는 동안 Multipart boundary parsing에서 오류가 발생했습니다. stackoverflow.com/questions/2823316/… 에서 논의 된 것처럼 string.letters에서 string.ascii_letters로 변경 해야했습니다. 경계에 대한 요구 사항은 여기에서 논의됩니다 : stackoverflow.com/questions/147451/…
amit

run_upload ({ 'server': '', 'thread': ''}, paths = [ '/ path / to / file.txt'])를 호출하면 "upload file"이 필요하기 때문에 upload_file (path) 행에 오류가 발생합니다. 3 개의 매개 변수를 사용하여이 행을 upload_file (경로, 1, 1)로 대체합니다
Radian

4

파일 객체에서 urlopen을 직접 사용하지 못하게하는 유일한 방법은 내장 파일 객체에 len 정의 가 없다는 것입니다 . 간단한 방법은 urlopen에 올바른 파일을 제공하는 서브 클래스를 작성하는 것입니다. 또한 아래 파일에서 Content-Type 헤더를 수정했습니다.

import os
import urllib2
class EnhancedFile(file):
    def __init__(self, *args, **keyws):
        file.__init__(self, *args, **keyws)

    def __len__(self):
        return int(os.fstat(self.fileno())[6])

theFile = EnhancedFile('a.xml', 'r')
theUrl = "http://example.com/abcde"
theHeaders= {'Content-Type': 'text/xml'}

theRequest = urllib2.Request(theUrl, theFile, theHeaders)

response = urllib2.urlopen(theRequest)

theFile.close()


for line in response:
    print line

@ robert Python2.7에서 코드를 테스트했지만 작동하지 않습니다. urlopen (Request (theUrl, theFile, ...))은 파일의 내용을 일반 게시물처럼 인코딩하지만 올바른 양식 필드를 지정할 수 없습니다. 변형 urlopen (theUrl, urlencode ({ 'serverside_field_name': EnhancedFile ( 'my_file.txt')}))조차 시도하지만 파일을 업로드하지만 <콘텐츠 파일 'my_file.txt'와 같이 잘못된 내용은 물론입니다! 0x00D6B718의 모드 'r'>. 내가 뭐 놓친 거 없니?
RayLuo

답변 해주셔서 감사합니다 . 위의 코드를 사용하여 PUT 요청을 사용하여 2.2GB 원시 이미지 파일을 웹 서버로 전송했습니다.
Akshay Patil


2

Chris Atlee의 포스터 라이브러리는이 작업에 특히 효과적입니다 (특히 편의 기능poster.encode.multipart_encode() ). 또한 전체 파일을 메모리에로드하지 않고도 대용량 파일 스트리밍을 지원합니다. Python issue 3244 도 참조하십시오 .


2

django rest api와 그 작업을 테스트하려고합니다.

def test_upload_file(self):
        filename = "/Users/Ranvijay/tests/test_price_matrix.csv"
        data = {'file': open(filename, 'rb')}
        client = APIClient()
        # client.credentials(HTTP_AUTHORIZATION='Token ' + token.key)
        response = client.post(reverse('price-matrix-csv'), data, format='multipart')

        print response
        self.assertEqual(response.status_code, status.HTTP_200_OK)

1
이 코드는 메모리 누수를 제공 close()합니다. 파일 을 잊었습니다 .
Chiefir

0

들어 httplib2를 살펴볼 수도 있습니다 . httplib2를 사용하는 것이 내장 HTTP 모듈을 사용하는 것보다 더 간결하다는 것을 알았습니다.


2
파일 업로드를 처리하는 방법을 보여주는 예제는 없습니다.
dland

링크가 오래되었습니다 + 인라인 예제가 없습니다.
jlr

3
이후 github.com/httplib2/httplib2 로 이동했습니다 . 반면에 요즘에는 아마 requests대신 권장 합니다.
pdc

0
def visit_v2(device_code, camera_code):
    image1 = MultipartParam.from_file("files", "/home/yuzx/1.txt")
    image2 = MultipartParam.from_file("files", "/home/yuzx/2.txt")
    datagen, headers = multipart_encode([('device_code', device_code), ('position', 3), ('person_data', person_data), image1, image2])
    print "".join(datagen)
    if server_port == 80:
        port_str = ""
    else:
        port_str = ":%s" % (server_port,)
    url_str = "http://" + server_ip + port_str + "/adopen/device/visit_v2"
    headers['nothing'] = 'nothing'
    request = urllib2.Request(url_str, datagen, headers)
    try:
        response = urllib2.urlopen(request)
        resp = response.read()
        print "http_status =", response.code
        result = json.loads(resp)
        print resp
        return result
    except urllib2.HTTPError, e:
        print "http_status =", e.code
        print e.read()
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.