从前端JavaScript向 Python Flask后端上传Microsoft Excel文件时出现错误
我正试图把一个MS Excel文件从前端的JS上传到后端的Python Flask,带有 fetch-api。但我收到了下面的错误消息。我对使用AJAX或 JS的其他方式不太熟悉 xmlhttprequest。

下面是我的HTML及 JS脚本:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!-- <link rel="stylesheet" href="styles.css"> -->
</head>
<body>
<h2>Upload a Document</h2>
<!-- File Input -->
<form method="POST">
<input type="file" id="fileInput" accept=".xlsx, .xls">
<button onclick="uploadDocument()"> Upload </button>
</form>
<p id="status"></p>
<script>
async function uploadDocument() {
const fileInput = document.getElementById('fileInput');
const status = document.getElementById('status');
if (fileInput.files.length === 0) {
status.innerText = "Please select a file first.";
return;
}
// Create form data to hold the file
const formData = new FormData();
formData.append('file', fileInput.files[0]);
try {
// Send the file to the Flask backend
const response = await fetch('http://127.0.0.1:6969/upload', {
method: 'POST',
body: formData
});
const result = await response.json();
console.log(result)
status.innerText = result.message || result.error;
} catch (error) {
status.innerText = "An error occurred during upload.";
console.error(error);
}
}
</script>
</body>
</html>
HTML文件是 docUpload.html。
Python代码如下:
om flask import Flask, render_template, jsonify, request
from flask_cors import CORS, cross_origin
from waitress import serve
import pandas as pd
import os
from werkzeug.utils import secure_filename
app = Flask(__name__)
CORS(app)
app.config['UPLOAD_FOLDER'] = 'upload'
@app.route('/')
@cross_origin()
def index():
return render_template("upload.html")
@app.route('/docUpload')
def addData():
return render_template("docUpload.html")
@app.route('/upload', methods = ['POST'])
def upload_excel():
if 'excel_file' not in request.files:
return jsonify({"error": "no file uploaded"}), 400
file = request.files['excel_file']
if file.filename == '':
return jsonify({"error": "No selected file"}),400
filename = secure_filename(file.filename)
dirname1 = os.path.dirname(__file__)
patha = os.path.abspath(dirname1),app.CONFIG['UPLOAD_FOLDER'],filename
os1 = os.path.join(patha)
file.save(os1)
if __name__ == "__main__":
serve(app, host='0.0.0.0', port=6969)
我不太确定自己哪里做错了。
解决方案
在用不同配置对代码进行测试后,我得出了与评论中的 @Herco相同的结论。
原始按钮仍然发送 <form>,但使用默认URL http://127.0.0.1:6969/
这会带来所有问题。
需要阻止按钮的默认行为。
一种方法是在按钮中使用 event.preventDefault()
<button onclick="event.preventDefault();uploadDocument(event)"> Upload </button>
更常见的方法是把 event 传给函数并在函数内部设定它
<button onclick="uploadDocument(event)"> Upload </button>
以及
function uploadDocument(event) {
event.preventDefault(); // stop sending standard form
另一种方法是将按钮设置为不同的类型 type="button",而不是默认的 type="submit"
<button type="button" onclick="uploadDocument()"> Upload </button>
Flask代码还有其他错误。
在Flask中你期望在 request.files['excel_file'] 得到 excel_file
但在JavaScript中你发送的是 formData.append('file', fileInput.files[0]); 并包含 file
应该是
formData.append('excel_file', fileInput.files[0]);
在 os.path.join() 中你使用 tuple patha,但 join 需要分开的值。
可能需要使用 * 来解包它
os.path.join(*patha)
但更易读的做法是直接设置它
os.path.join(dirname, upload_folder, filename)
你有 app.config 和 app.CONFIG。
保存文件后你仍然需要输出一些内容/JSON,例如
return jsonify({"message": "OK"})
用于测试的完整代码:
main.py
from flask import Flask, render_template, jsonify, request
from flask_cors import CORS, cross_origin
from waitress import serve
import pandas as pd
import os
from werkzeug.utils import secure_filename
app = Flask(__name__)
CORS(app)
app.config["UPLOAD_FOLDER"] = "upload"
@app.route("/")
@cross_origin()
def index():
return render_template("upload.html")
@app.route("/docUpload")
def addData():
return render_template("docUpload.html")
@app.route("/upload", methods=["POST"])
def upload_excel():
print("upload")
print(request.files)
if "excel_file" not in request.files:
return jsonify({"error": "no file uploaded"}), 400
file = request.files["excel_file"]
if file.filename == "":
return jsonify({"error": "No selected file"}), 400
dirname = os.path.abspath(os.path.dirname(__file__))
upload_dir = app.config["UPLOAD_FOLDER"]
filename = secure_filename(file.filename)
os.makedirs(os.path.join(dirname, upload_dir), exist_ok=True)
fullpath = os.path.join(dirname, upload_dir, filename)
file.save(fullpath)
return jsonify({"message": "OK"})
if __name__ == "__main__":
PORT = 6969
print(f"http://localhost:{PORT}")
serve(app, host="0.0.0.0", port=PORT)
# app.run(host="0.0.0.0", port=PORT, debug=True)
templates/upload.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!-- <link rel="stylesheet" href="styles.css"> -->
</head>
<body>
<h2>Upload a Document</h2>
<hr>
<h3>Pure HTML without JavaScript</h3>
<!-- File Input -->
<form method="POST" action="/upload" enctype="multipart/form-data">
<input type="file" name="excel_file" accept=".xlsx, .xls">
<button type="submit"> Upload </button>
</form>
<hr>
<h3>With JavaScript</h3>
<!-- File Input -->
<form method="POST">
<input type="file" id="fileInput" accept=".xlsx, .xls">
<button type="button" onclick="uploadDocument(event)"> Upload </button><br>
<button type="button" onclick="asyncUploadDocument(event)"> Async Upload </button><br>
<button onclick="event.preventDefault();asyncUploadDocument()"> Event ; Async Upload </button>
</form>
<hr>
<p id="status"></p>
<hr>
<script>
function uploadDocument(event) {
//event.preventDefault(); // stop sending standard form
console.log("uploadDocument");
const fileInput = document.getElementById('fileInput');
const status = document.getElementById('status');
console.log("fileInput.files.length:", fileInput.files.length)
if (fileInput.files.length === 0) {
status.innerText = "Please select a file first.";
return;
}
console.log("create FormData");
// Create form data to hold the file
const formData = new FormData();
formData.append('excel_file', fileInput.files[0]);
const url = "http://localhost:6969/upload";
// const url = "http://httpbingo.org/post";
console.log("start fetch", url);
// Send the file to the Flask backend
fetch(url, {
method: 'POST',
body: formData
})
.then(
response => response.json(),
error => console.log("Wrong response without JSON")
)
.then(result => {
console.log("response");
console.log(result);
status.innerText = result.message || result.error;
})
.catch(error => {
status.innerText = "An error occurred during upload.";
console.error(error);
});
}
async function asyncUploadDocument(event) {
//event.preventDefault(); // stop sending standard form
console.log("asyncUploadDocument");
const fileInput = document.getElementById('fileInput');
const status = document.getElementById('status');
console.log("fileInput.files.length:", fileInput.files.length)
if (fileInput.files.length === 0) {
status.innerText = "Please select a file first.";
return;
}
console.log("create FormData");
// Create form data to hold the file
const formData = new FormData();
formData.append('excel_file', fileInput.files[0]);
const url = "http://localhost:6969/upload";
// const url = "http://httpbingo.org/post";
console.log("start fetch", url);
try {
// Send the file to the Flask backend
const response = await fetch(url, {
method: 'POST',
body: formData
})
const result = await response.json();
console.log("result");
console.log(result);
status.innerText = result.message || result.error;
} catch (error) {
status.innerText = "An error occurred during upload.";
console.error(error);
}
}
</script>
</body>
</html>
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。