我有一个包含文件输入元素的 HTML 表单。提交表单时,会执行一个 JS 函数来检查上传的文件是否为 PDF 文件且是否小于 10MB。所有这些都可以正常工作,并且正确的文件大小会记录在控制台中。
然后JS函数通过HTTP POST请求将文件发送到Python服务器。但是,发送的文件始终是空对象/字典。当我在发送之前
console.log(file);
时,所有文件数据都会被记录,但是当我查看检查器中的“网络”选项卡时,它只是一个空对象/字典:
但是,当我更改 HTML 表单以便将数据直接发送到 Python CGI 脚本而不先调用 JS 函数时,它工作得很好。
这是我的 HTML 代码:
<!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 代码:
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()
问题是
JSON.stringify
- 它无法将 File
对象转换为 JSON
。
如果您确实必须将其作为 JSON 发送,那么您可能需要使用
FileReader
和函数 readAsDataUri
将文件内容转换为 Base64
最小工作示例。
async function addFile() {
const formData = new FormData(document.forms["DataForm"]);
const file = formData.get('file_contract');
const reader = new FileReader();
reader.onload = () => {
//const data = {'answers': reader.result} // send only file contemt
const data = {'answers':
{
'data': reader.result, // file content as `Base64`
'name': file.name,
'size': file.size,
'type': file.type,
'lastModified': file.lastModified,
}
}
fetch("/addRecord", {
method: "POST",
headers: {
//'Accept': 'application/json, text/plain, */*', // expect JSON or TEXT as response from server
'Content-Type': 'application/json; charset=UTF-8' // inform server that you send JSON
},
body: JSON.stringify(data),
}).then((response) => {
if(response.ok) {
const responseData = response.json();
//TODO: use responseData
}
});
}
reader.readAsDataURL(file); // convert to `Base64` and execute `onload()`
}