So, curl -F ...
is basically emulating submitting a form.
-F, --form <name=content>
(HTTP SMTP IMAP) For HTTP protocol family, this lets curl emulate a filled-in form in which a user has pressed the submit button. This causes curl to POST data using the Content-Type multipart/form-data according to RFC 2388.
You can replicate this in Python using the requests
module (note that you will need to install it - e.g., pip3 install requests
):
import requests
with open("the_logo.png", "rb") as file:
file_content = file.read()
response = requests.put(
"http://localhost:8080",
files={"logo": file_content},
headers={
"X-API-Key": "API_KEY",
"Accept": "application/json"
}
)
Or, although not really straightforward, using the built-in urllib.request
module:
import io
from urllib.request import urlopen, Request
import uuid
with open("the_logo.png", "rb") as file:
file_content = file.read()
boundary = uuid.uuid4().hex.encode("utf-8")
buffer = io.BytesIO()
buffer.write(b"--" + boundary + b"\r\n")
buffer.write(b"Content-Disposition: form-data; name=\"logo\"; filename=\"logo\"\r\n")
buffer.write(b"\r\n")
buffer.write(file_content)
buffer.write(b"\r\n")
buffer.write(b"--" + boundary + b"--\r\n")
data = buffer.getvalue()
request = Request(url="http://localhost:8080", data=data, method="PUT")
request.add_header("X-API-Key", "API_KEY")
request.add_header("Accept", "application/json")
request.add_header("Content-Length", len(data))
request.add_header("Content-Type", f"multipart/form-data; boundary={boundary.decode('utf-8')}")
with urlopen(request) as response:
response_body = response.read().decode("utf-8")
If you use for example netcat to listen to the used port, you should see that both curl -F ...
and the above scripts generate something like the following:
$ nc -l -p 8080
PUT / HTTP/1.1
Host: localhost:8080
X-Api-Key: API_KEY
Accept: application/json
Content-Length: [DATA_LENGTH]
Content-Type: multipart/form-data; boundary=[GENERATED_BOUNDARY]
Connection: close
--[GENERATED_BOUNDARY]
Content-Disposition: form-data; name="logo"; filename="logo"
[PNG_BYTES]
--[GENERATED_BOUNDARY]--
NOTE: For a more complete urllib.request
implementation see https://pymotw.com/3/urllib.request/#uploading-files
file_obj
itself be in thedata
location in the tuple (if you haven't read from it and the pointer is still at the front of the file, so you'd need to take out thedata = file_obj.read()
line). – Deucalionfiles={"logo": ("the_logo.png", open('the_logo.png', 'rb'), "image/png")}
once. – Pontificals