Send file using POST from a Python script

PythonPostFile UploadHttp Post

Python Problem Overview


Is there a way to send a file using POST from a Python script?

Python Solutions


Solution 1 - Python

From: https://requests.readthedocs.io/en/latest/user/quickstart/#post-a-multipart-encoded-file

Requests makes it very simple to upload Multipart-encoded files:

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

That's it. I'm not joking - this is one line of code. The file was sent. Let's check:

>>> 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": ""
}

Solution 2 - Python

Yes. You'd use the urllib2 module, and encode using the multipart/form-data content type. Here is some sample code to get you started -- it's a bit more than just file uploading, but you should be able to read through it and see how it works:

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

Solution 3 - Python

Looks like python requests does not handle extremely large multi-part files.

The documentation recommends you look into requests-toolbelt.

Here's the pertinent page from their documentation.

Solution 4 - Python

The only thing that stops you from using urlopen directly on a file object is the fact that the builtin file object lacks a len definition. A simple way is to create a subclass, which provides urlopen with the correct file. I have also modified the Content-Type header in the file below.

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

Solution 5 - Python

Chris Atlee's poster library works really well for this (particularly the convenience function poster.encode.multipart_encode()). As a bonus, it supports streaming of large files without loading an entire file into memory. See also Python issue 3244.

Solution 6 - Python

I am trying to test django rest api and its working for me:

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)

Solution 7 - Python

pip install http_file

#импорт вспомогательных библиотек
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
import requests
#импорт http_file
from http_file import download_file
#создание новой сессии
s = requests.Session()
#соеденение с сервером через созданную сессию
s.get('URL_MAIN', verify=False)
#загрузка файла в 'local_filename' из 'fileUrl' через созданную сессию
download_file('local_filename', 'fileUrl', s)

Solution 8 - Python

You may also want to have a look at httplib2, with examples. I find using httplib2 is more concise than using the built-in HTTP modules.

Solution 9 - Python

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionreadonlyView Question on Stackoverflow
Solution 1 - PythonPiotr DobrogostView Answer on Stackoverflow
Solution 2 - PythonJohn MillikinView Answer on Stackoverflow
Solution 3 - PythonryeView Answer on Stackoverflow
Solution 4 - PythonilmarinenView Answer on Stackoverflow
Solution 5 - PythongotgenesView Answer on Stackoverflow
Solution 6 - PythonRanvijay SachanView Answer on Stackoverflow
Solution 7 - PythonСтанислав ТышкоView Answer on Stackoverflow
Solution 8 - PythonpdcView Answer on Stackoverflow
Solution 9 - Pythonuser6081103View Answer on Stackoverflow