Currently trying to use HTTP Request node to send an API key and an image from user’s pc to an image upload site. I could get the API Key to the API fine, but that was using application/x-www-form-urlencoded. The issue is, when I switched to multipart/form-data, I can no longer get the api-key to send, I’m not sure if it’s a syntax error on my end, but there is no up to date syntax references for this issue. I’ll provide what I have, but it is clearly wrong, I just have no clue where at. Heres the code, you should be able to replicate it pretty easily. My syntax is for sure wrong, but there’s no real proper examples that I can find, at least for GDscript on this matter.
var http_request = HTTPRequest.new()
var api_key = "MYAPIKEY"
var send_url = "https://www.imghippo.com/v1/upload"
var file
var content = Image.load_from_file("user://20240520_160400.jpg")
var datalist:PackedByteArray = []
var boundary = "testboundary"
var fileb64
func _ready():
http_request.request_completed.connect(_on_request_completed)
add_child(http_request)
send_image()
func send_image():
datalist.append_array(('--testboundary').to_utf8_buffer())
datalist.append_array(('Content-Disposition: form-data; name=file; filename=user://20240520_160400.jpg').to_utf8_buffer())
datalist.append_array(('Content-Type: application/octet-stream').to_utf8_buffer())
datalist.append_array(('').to_utf8_buffer())
#file = Image.load_from_file('user://20240520_160400.jpg').get_data()
#fileb64 = Marshalls.raw_to_base64(file)
#datalist.append_array((content).)
datalist.append_array(('--testboundary').to_utf8_buffer())
datalist.append_array(('Content-Disposition: form-data; name=api_key;').to_utf8_buffer())
datalist.append_array(('Content-Type: text/plain').to_utf8_buffer())
datalist.append_array(('').to_utf8_buffer())
datalist.append_array(('MYAPIKEY').to_utf8_buffer())
datalist.append_array(('--testboundary--').to_utf8_buffer())
datalist.append_array(('').to_utf8_buffer())
var headers = ["Content-Type: multipart/form-data; boundary=testboundary"]
http_request.request(send_url, headers, HTTPClient.METHOD_POST, str(datalist))
func _on_request_completed(results, response_code, headers, body):
var requestbody = JSON.stringify(body.get_string_from_utf8())
print(requestbody)
I tested this with a local server. I have not checked if the file is uploaded correctly or not. I’m also not sure how to send it as a binary encoding so I’m using a base64 one.
func send_image() -> void:
# Create some random bytes to generate our boundary value
var crypto = Crypto.new()
var random_bytes = crypto.generate_random_bytes(16)
var boundary = '--GODOT%s' % random_bytes.hex_encode()
# Setup the header Content-Type with our bundary
var headers = [
'Content-Type: multipart/form-data;boundary=%s' % boundary
]
# Load the image and get the png buffer
var image = load("res://letter.png").get_image() as Image
var buffer = image.save_png_to_buffer()
# Create our body
var body = PackedStringArray()
body.push_back("--{{boundary}}")
body.push_back('Content-Disposition: form-data; name="api_key"')
body.push_back('')
body.push_back("MY_API_KEY") # The API key you have
body.push_back("--{{boundary}}")
body.push_back('Content-Disposition: form-data; name="image"; filename="my_image.png"')
body.push_back('Content-Type: image/png')
body.push_back('Content-Transfer-Encoding: base64') # Encode is base64
body.push_back('')
body.push_back(Marshalls.raw_to_base64(buffer)) # Convert the buffer to a base64 String
body.push_back("--{{boundary}}--")
# Finally, join the body with CRLF and insert our boundary
var final_string = "\r\n".join(body).format({"boundary": boundary}, "{{_}}")
http_request.request('http://localhost:5173/upload', headers, HTTPClient.METHOD_POST, final_string)
HTTP request body uses CRLF (the \r\n part) to separate the lines.
Each field has this structure:
--boundary
Header of that field (Content-Disposition, Content-Type,…)
Empty line
The content of the field
The body ends with --boundary-- (notice the last -- characters)
Thank you so much for the reply! After seeing this and implementing it into my code, it still wasn’t working, I was getting a response from the server, but kept getting txt files back. The website I was using only supports binary data, and as far as how to get that in Godot, I am not sure. I was trying to send the bytes as a string, but it kept giving .BIN or .txt files back. My advice to anyone going through a similar situation, set it up just as @mrcdk did, but make sure the API you are using supports Base64 data transfer. My recommendation is imgbb, as it is doing everything I needed it for. Shout out to @mrcdk, I did over 8 hours of research trying to find out how to get the syntax properly working, but there just isn’t a lot of resources I could find, especially for Godot implementation. ALSO: if instead of wanting to access your res://, you can get image data straight from your directory. Here is the code:
var send_url = "YOURAPIURL"
func send_image() -> void:
# Create some random bytes to generate our boundary value
var crypto = Crypto.new()
var random_bytes = crypto.generate_random_bytes(16)
var boundary = '--GODOT%s' % random_bytes.hex_encode()
# Setup the header Content-Type with our boundary
var headers = [
'Content-Type: multipart/form-data;boundary=%s' % boundary
#Get file as bytes,
var image = FileAccess.get_file_as_bytes("user://YOURFILENAME.jpg")
#proceed with code from @mrcdk, mine has a few tweaks, such as getting rid of filename, and using /jpeg instead of /png, and just make sure you tweak your form-data names to match your APIS proper parameters.
var body = PackedStringArray()
body.push_back("--{{boundary}}")
body.push_back('Content-Disposition: form-data; name="key"')
body.push_back('')
body.push_back("YOURAPIKEY") # The API key you have
body.push_back("--{{boundary}}")
body.push_back('Content-Disposition: form-data; name="image"')
body.push_back('Content-Type: image/jpeg')
body.push_back('Content-Transfer-Encoding: base64') # base64 encode
body.push_back('')
body.push_back(Marshalls.raw_to_base64(image)) #Marshalls will encode your bytes to base64, then should send just like original example
body.push_back("--{{boundary}}--")
var final_string = "\r\n".join(body).format({"boundary": boundary}, "{{_}}")
http_request.request(send_url, headers, HTTPClient.METHOD_POST, final_string)