I have an HTML form that has a file input element. When submitting the form, a JS function is executed which checks if the uploaded file is a PDF file and is less than 10MB. All of this works correctly and the correct filesize is logged in the console.
Then the JS function sends the file to the Python server through an HTTP POST request. However, the sent file is always an empty object / dictionary. When I console.log(file);
before it is sent, all the file data is logged but when I look at the Network tab in the inspector, it’s just an empty object / dictionary:
However, when I change my HTML form so it sends the data directly to the Python CGI script without calling the JS function first, it works perfectly.
Here’s my HTML code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script src="./js/new.js"></script>
<title>test</title>
</head>
<body>
<script></script>
<header>
<h1>File upload test</h1>
</header>
<main>
<fieldset>
<form
action="javascript:addFile()"
id="DataForm"
>
<div>
<label for="file_contract">contract:</label
><input
type="file"
accept="application/pdf"
id="file_contract"
name="file_contract"
/>
</div>
<button type="button" class="outlined" onclick="cancel()">
Annuleer
</button>
<input type="submit" value="Bewaar" />
</form>
</fieldset>
</main>
<script>
function cancel() {
const host = window.location.host;
const redirectUrl = `http://${host}`;
window.location.replace(redirectUrl);
}
</script>
</body>
</html>
JavaScript code:
async function addFile() {
const formData = new FormData(document.forms["DataForm"]);
// get uploaded files
// Function to check and append a file to the formData
function handleFileUpload(fileInputId) {
console.log("uploading file...");
const fileInput = document.getElementById(fileInputId);
const file = fileInput.files[0];
// Check if a file is selected
// Ensure the file is a PDF
if (file.type === "application/pdf") {
console.log(`filesize = ${file.size}`);
// 10MB in bytes
formData.append(fileInputId, fileInput.files[0]);
}
return true;
}
// Handle each file upload separately
if (!handleFileUpload("file_contract")) {
console.log("form submission prevented");
return false; // Prevent form submission
}
const data = {
answers: Object.fromEntries(formData),
};
console.log(data["answers"]["file_contract"]);
try {
const response = await fetch("../../cgi-bin/addRecord.py", {
method: "POST",
headers: {
"Content-type": "application/json; charset=UTF-8",
},
body: JSON.stringify(data),
});
const responseData = await response.json();
if (responseData.hasOwnProperty("status")) {
if (responseData["status"] === "failed") {
// session is invalid / expired
alert("Login sessie vervallen. Log opnieuw in.");
} else if (responseData.status === "success") {
const host = window.location.host;
const redirectUrl = `http://${host}`;
console.log("redirecting... (commented out)");
// window.location.replace(redirectUrl);
} else {
alert(
"Fout bij het opslaan. Probeer het nog eens of contacteer Sandra."
);
}
}
} catch (error) {
console.error(error);
alert("Fout bij het opslaan. Probeer het nog eens of contacteer Sandra.");
}
}
Python CGI:
#!/usr/bin/env python3
import cgi
import hashlib
import json
import os
import sys
import logging
# Get the absolute path of the current script
dirname = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
if os.path.exists(f'{dirname + "/log.log"}'):
# If it exists, open it in write mode to clear its contents
with open(f'{dirname + "/log.log"}', 'w'):
pass
logging.basicConfig(filename=f'{dirname + "/log.log"}', level=logging.DEBUG)
def generate_response(status, data=None):
response = {"status": status}
if data:
response["data"] = data
print("Content-Type: application/json")
print()
print(json.dumps(response))
def calculate_file_hash(file_data):
# Create an MD5 hash object
md5_hash = hashlib.md5()
# Read the file data in chunks to efficiently handle large files
for chunk in iter(lambda: file_data.read(4096), b''):
md5_hash.update(chunk)
# Return the hexadecimal representation of the MD5 hash
return md5_hash.hexdigest()
def main():
# read form data from HTTP POST request
data = sys.stdin.read(int(os.environ.get('CONTENT_LENGTH', 0)))
post_data = json.loads(data)
answers = post_data["answers"]
logging.debug(str(answers))
if "file_contract" in answers:
contract_file = answers['file_contract']
contract_file_filename = contract_file.filename
contract_file_hash = calculate_file_hash(contract_file.file)
# save the file data to the werkmap
# Save the PDF file to a folder
contract_filename_path = os.path.join(dirname, "documenten", contract_file_hash)
with open(contract_filename_path, 'wb') as contract_file_handle:
contract_file_handle.write(contract_file.file.read())
generate_response("success")
if __name__ == "__main__":
main()