# \[BUG-305\] Trying to send multipart/form-data

**URL:** <https://forum.inductiveautomation.com/t/bug-305-trying-to-send-multipart-form-data/57764>\
**Category:** Ignition\
**Created:** [March 29, 2022, 5:07pm UTC](https://forum.inductiveautomation.com/t/bug-305-trying-to-send-multipart-form-data/57764 "2022-03-29T17:07:19Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![m.sanders](https://avatars.discourse-cdn.com/v4/letter/m/df705f/32.png) [@m.sanders](https://forum.inductiveautomation.com/u/m.sanders)\
**Post date:** [March 29, 2022, 5:07pm UTC](https://forum.inductiveautomation.com/t/bug-305-trying-to-send-multipart-form-data/57764/1 "2022-03-29T17:07:19Z")

</div>

I am trying to integrate Ignition with a knowledgebase software, BookStack, and would like to send it some of the reports that we generate.

Bookstack’s HTTP API allows this, but requires the data comes through as Content-Type of multipart/form-data.

From what I have now read from other forum posts and the Ignition docs, neither system.net.httpPost or system.net.httpClient and the corresponding .post() function do not support multipart/form-data.

Is there a function or a library that I can use to send multipart/form-data?

---

<div class="post-metadata">

**Author:** ![stat](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/stat/32/32530_2.png) [@stat](https://forum.inductiveautomation.com/u/stat)\
**Post date:** [March 29, 2022, 7:22pm UTC](https://forum.inductiveautomation.com/t/bug-305-trying-to-send-multipart-form-data/57764/2 "2022-03-29T19:22:23Z")

</div>

You’re correct that system.net.httpPost and system.net.httpClient with .post() do not support multipart/form-data at this time. We currently have an internal ticket for this, but there is not a specific timeline for when it will be done.

You can use the python 2.7.0 requests module to run an HTTP request for multipart/form-data. It can be downloaded here:  
Python 2.7.0 Requests Module: [requests · PyPI](https://pypi.org/project/requests/2.7.0/)

To import this module, you would take the “requests” folder from the source distribution and add it to the Ignition install directory and restart the gateway.  
For more information on those steps, you can reference this [article](https://support.inductiveautomation.com/hc/en-us/articles/360047136012-Importing-and-using-3rd-party-Python-libraries-in-Ignition#:~:text=To%20import%20the%20library%20into,located%20inside%20the%20installation%20directory.&text=Now%20you%20are%20ready%20to,of%20your%20newly%20imported%20library!).

---

<div class="post-metadata">

**Author:** ![m.sanders](https://avatars.discourse-cdn.com/v4/letter/m/df705f/32.png) [@m.sanders](https://forum.inductiveautomation.com/u/m.sanders)\
**Post date:** [April 8, 2022, 6:48pm UTC](https://forum.inductiveautomation.com/t/bug-305-trying-to-send-multipart-form-data/57764/3 "2022-04-08T18:48:35Z")

</div>

Sorry to reply so late to this, we have added the requests module and restarted the gateway and are getting an  
`ImportError: No module named urllib3`  
when trying to import the requests module

---

<div class="post-metadata">

**Author:** ![stat](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/stat/32/32530_2.png) [@stat](https://forum.inductiveautomation.com/u/stat)\
**Post date:** [April 8, 2022, 7:54pm UTC](https://forum.inductiveautomation.com/t/bug-305-trying-to-send-multipart-form-data/57764/4 "2022-04-08T19:54:33Z")

</div>

The Python requests module requires the urllib3 module and depending on which version you downloaded, you’ll have to add it yourself.  
Around version 2.16.0, urllib3 stopped being bundled with the requests module according to their release logs ([Community Updates — Requests 2.27.1 documentation](https://docs.python-requests.org/en/latest/community/updates/)).

Based on those release notes, if you download a version prior to that one then you won’t encounter that error as the urlib3 module will be included in the requests folder under a folder named “packages” otherwise you’ll need to downloaded the respective urllib3 module that is supported by the request module as stated in the release notes. In addition to urlib3, you would also require the chardet module to use requests.

 ![requests](https://us1.discourse-cdn.com/inductiveautomation/original/3X/f/1/f13fcbc0afa1261c4ba6e067bd436ff40cd34918.png)

As Python 3.4 support ended for requests at 2.22.0, you may run into some issues with any version of requests above that. For your information as well, adding a python module in pylib doesn’t require you to restart the gateway, but you will have to restart any active Designer or Vision Clients to receive any updates to the pylib folder.

---

<div class="post-metadata">

**Author:** ![swanand15.kul](https://avatars.discourse-cdn.com/v4/letter/s/278dde/32.png) [@swanand15.kul](https://forum.inductiveautomation.com/u/swanand15.kul)\
**Post date:** [May 17, 2023, 11:35pm UTC](https://forum.inductiveautomation.com/t/bug-305-trying-to-send-multipart-form-data/57764/5 "2023-05-17T23:35:45Z")

</div>

Hello, curious if the change that would accept multipart/form-data has been implemented yet. Thanks!

---

<div class="post-metadata">

**Author:** ![tyler.bennett](https://avatars.discourse-cdn.com/v4/letter/t/f04885/32.png) [@tyler.bennett](https://forum.inductiveautomation.com/u/tyler.bennett)\
**Post date:** [July 27, 2023, 5:36pm UTC](https://forum.inductiveautomation.com/t/bug-305-trying-to-send-multipart-form-data/57764/6 "2023-07-27T17:36:50Z")

</div>

Here is an example function for performing "multipart/form-data" POST (file upload) using the [urllib2](https://docs.python.org/2/library/urllib2.html) library (it uses only built-in libraries, does not require any external packages).

Note that the [urllib2 documentation](https://docs.python.org/2/library/urllib2.html) suggests "The [Requests package](http://requests.readthedocs.org/) is recommended for a higher-level HTTP client interface" but I was aiming for a solution that did not require import of any external packages, and this works.

This version is hard-coded for a content type of "audio/mpeg" but could be tweaked to use another content type.

Fill disclosure, this code was created with the assistance of ChatGPT after a good amount of coaxing (updated prompts) and manual tweaks.

My specific use case is uploading sound files to an [Axis network speaker](https://www.axis.com/products/network-speakers) using it's [Media Clip API](https://www.axis.com/vapix-library/subjects/t10100065/section/t10036045/display) within Ignition v8.1.28+.

```python
import os
import urllib2
import base64

def upload_file(url, file_path, username, password):
    if not os.path.isfile(file_path):
        print("Error: File not found at {}".format(file_path))
        return

    try:
        # Open the file for reading
        with open(file_path, 'rb') as file_stream:
            file_data = file_stream.read()

        # Prepare the request
        request = urllib2.Request(url)
        boundary = "-----" + hex(hash(file_data))[2:]
        request.add_header("Content-Type", "multipart/form-data; boundary={}".format(boundary))

        # Encode credentials for basic authentication
        credentials = base64.b64encode("{}:{}".format(username, password))
        request.add_header("Authorization", "Basic {}".format(credentials))

        # Build the request body
        body = b''
        body += b"--" + boundary + b"\r\n"
        body += b"Content-Disposition: form-data; name=\"file\"; filename=\"{}\"\r\n".format(os.path.basename(file_path))
        body += b"Content-Type: audio/mpeg\r\n\r\n"
        body += file_data
        body += b"\r\n--" + boundary + b"--\r\n"

        # Set the request method and data
        request.get_method = lambda: 'POST'
        request.add_data(body)

        # Send the request
        response = urllib2.urlopen(request)

        # Print debug information, see https://docs.python.org/2/library/urllib2.html#urllib2.urlopen
        print " **DEBUG INFO - START, comment out in final code**"
        print "** URL: ", response.geturl()
        print "** Code: ", response.getcode()
        print "** Info: \n", response.info()
        print "** DEBUG INFO - END\n"

        # Get the response code
        response_code = response.getcode()
        if response_code == 200:
            print("File uploaded successfully.")
        else:
            print("Error: Failed to upload file. Response code: {}".format(response_code))
		
    except Exception as e:
        print("Error: {}".format(e))

```

---

<div class="post-metadata">

**Author:** ![S\_ANIL\_KUMAR](https://avatars.discourse-cdn.com/v4/letter/s/898d66/32.png) [@S\_ANIL\_KUMAR](https://forum.inductiveautomation.com/u/S_ANIL_KUMAR)\
**Post date:** [September 27, 2023, 11:34am UTC](https://forum.inductiveautomation.com/t/bug-305-trying-to-send-multipart-form-data/57764/7 "2023-09-27T11:34:49Z")

</div>

Hello bennett, I tried using the above code to add attachments to the jira issues using jira's rest api. I am able add images that are below 50kb where as above 50kb i see the image broken in jira's issue. Could anyone help what's going wrong with the code.

from base64 import b64encode  
import os  
import urllib2

jira\_url = "[https://domain-name/rest/api/2/issue/{issueID}/attachments](https://domain-name/rest/api/2/issue/%7BissueID%7D/attachments)"  
username = "[email.com](http://email.com)"  
api\_token = "api\_token"  
auth =b64encode(username +":"+api\_token)

file\_path = system.file.openFile()  
with open(file\_path, 'rb') as file\_stream:  
file\_data = file\_stream.read()

# Prepare the request

request = urllib2.Request(jira\_url)  
boundary = "----" + hex(hash(file\_data))[2:]  
request.add\_header("Content-Type", "multipart/form-data; boundary={}".format(boundary))

# Encode credentials for basic authentication

request.add\_header("Authorization", "Basic {}".format(auth))  
request.add\_header("X-Atlassian-Token","no-check")

# Build the request body

body = b''  
body += b"--" + boundary + b"\r\n"  
body += b"Content-Disposition: form-data; name="file"; filename="{}"\r\n".format(os.path.basename(file\_path))  
body += b"Content-Type: multipart/form-data;boundary:{}\r\n\r\n".format(boundary)  
body += file\_data  
body += b"\r\n--" + boundary + b"--\r\n"

# Set the request method and data

request.get\_method = lambda: 'POST'  
request.add\_data(body)

# Send the request

response = urllib2.urlopen(request)  
print response.getcode()  
print response.info()

---

<div class="post-metadata">

**Author:** ![S\_ANIL\_KUMAR](https://avatars.discourse-cdn.com/v4/letter/s/898d66/32.png) [@S\_ANIL\_KUMAR](https://forum.inductiveautomation.com/u/S_ANIL_KUMAR)\
**Post date:** [October 3, 2023, 3:35pm UTC](https://forum.inductiveautomation.com/t/bug-305-trying-to-send-multipart-form-data/57764/8 "2023-10-03T15:35:32Z")

</div>

Hi @tyler.bennett , Could you please let me know you're opinion on the above code. I am not finding a way to send images that are above 50kb.

Thanks!

---

<div class="post-metadata">

**Author:** ![Kenneth\_Wainwright](https://avatars.discourse-cdn.com/v4/letter/k/e99b99/32.png) [@Kenneth\_Wainwright](https://forum.inductiveautomation.com/u/Kenneth_Wainwright)\
**Post date:** [December 13, 2024, 9:19pm UTC](https://forum.inductiveautomation.com/t/bug-305-trying-to-send-multipart-form-data/57764/9 "2024-12-13T21:19:54Z")

</div>

For someone in the future... I was able to overcome this problem with a microsoft authentication server by using the Content Type 'x-www-form-urlencoded'.

This may not be a solution for OP's problem, but it worked well in my situation.

Using Postman can help a ton in formatting this string, since it is not as readable as JSON or multipart form data
