从JS向Python CGI上传文件始终返回空字典问题
问题根源
- File对象无法JSON序列化:
JSON.stringify不支持处理File类型数据,会将其转为空对象{},导致服务器接收不到文件内容。 - 请求格式不匹配:手动设置
Content-type: application/json,但文件上传需要用表单默认的multipart/form-data编码,两种格式的处理逻辑完全不同。
修复步骤
1. 修改JavaScript代码
直接发送FormData对象(无需转为JSON),移除手动设置的Content-type头(浏览器会自动为FormData设置正确的multipart/form-data头及边界标识):
async function addFile() { const formData = new FormData(document.forms["DataForm"]); function handleFileUpload(fileInputId) { console.log("uploading file..."); const fileInput = document.getElementById(fileInputId); const file = fileInput.files[0]; if (!file) { console.log("No file selected"); return false; } // 验证PDF格式和10MB大小限制 if (file.type !== "application/pdf") { alert("请上传PDF格式文件"); return false; } const maxSize = 10 * 1024 * 1024; // 10MB if (file.size > maxSize) { alert("文件大小不能超过10MB"); return false; } console.log(`filesize = ${file.size}`); return true; } if (!handleFileUpload("file_contract")) { console.log("form submission prevented"); return false; } try { const response = await fetch("../../cgi-bin/addRecord.py", { method: "POST", body: formData, }); const responseData = await response.json(); if (responseData.hasOwnProperty("status")) { if (responseData["status"] === "failed") { 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."); } }
2. 修改Python CGI代码
使用cgi模块解析multipart/form-data格式的表单数据,和表单直接提交的处理逻辑保持一致:
#!/usr/bin/env python3 import cgi import hashlib import json import os 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"}'): 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): md5_hash = hashlib.md5() for chunk in iter(lambda: file_data.read(4096), b''): md5_hash.update(chunk) return md5_hash.hexdigest() def main(): try: # 解析multipart/form-data格式的表单数据 form = cgi.FieldStorage() if "file_contract" in form: contract_file = form["file_contract"] # 确认是文件上传 if contract_file.file: contract_file_filename = contract_file.filename contract_file_hash = calculate_file_hash(contract_file.file) # 重置文件指针到开头(计算哈希时已读取过一次) contract_file.file.seek(0) 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") except Exception as e: logging.error(str(e)) generate_response("failed") if __name__ == "__main__": main()
额外说明
- 直接表单提交正常的原因:表单默认使用
multipart/form-data编码,cgi.FieldStorage可以正确解析文件;而JS转JSON发送时,File对象被序列化为空对象,服务器无法获取有效数据。 - 验证逻辑保留:JS中的PDF格式和大小检查依然生效,确保不符合要求的文件不会被发送到服务器。
内容的提问来源于stack exchange,提问作者Mr_BananaPants
相关产品推荐
相关产品推荐

