Flask部署XGBoost模型时无法上传、保存及读取CSV文件的问题排查求助
Hey there! Let's walk through the issues in your code step by step—there are a few key bugs preventing your CSV from saving correctly and the download from working:
1. Incorrect File Path Construction in predict()
Your pathout lines have multiple critical issues:
- You’re using
os.path.join('uploads', path, '_out.csv'.format(n)), butpathalready includes theuploads/prefix (since you created it withos.path.join('uploads', secrets.token_hex(8))). This would create a invalid doubleuploads/uploads/...path that doesn’t exist. - The
format(n)is broken:nisn’t defined anywhere, and you’re trying to format an empty string. Instead, you need to append_out.csvto the original unique filename (not the full path).
Fix it by extracting the base filename and building the correct output path:
# Inside predict() function filename = os.path.basename(path) pathout = os.path.join('uploads', f"{filename}_out.csv")
2. Flask Route Method Mismatch
Your /csvOut route is set to methods=['POST'], but redirect(url_for('csvOut', ...)) sends a GET request. This will trigger a 405 Method Not Allowed error. Update the route to accept GET requests:
@app.route("/csvOut", methods=['GET']) def csvOut(): # ... rest of your code
3. Missing Static File Access for Uploads
Flask doesn’t automatically serve files from the uploads directory. To make the download link work, add a dedicated route to serve uploaded files:
from flask import send_from_directory @app.route('/uploads/<filename>') def uploaded_file(filename): return send_from_directory('uploads', filename)
Then update your out.html download link to use this route:
<a href="{{ url_for('uploaded_file', filename=download_filename) }}" download><button>Download File</button></a>
4. Fragile Column Name Matching
Comparing the entire column list with an exact, ordered match is fragile—extra whitespace, case differences, or column reordering will break it. Use a subset check to verify required columns exist (regardless of order):
# For model4 required_cols_4 = {'Leukocytes', 'Platelets', 'Eosinophils', 'Monocytes'} if required_cols_4.issubset(data.columns): data['Prediction'] = model4.predict(data) out = 1 # For model14 required_cols_14 = {'Hematocrit', 'Hemoglobin', 'Platelets', 'MPV', 'Erythrocytes', 'Lymphocytes', 'MCHC', 'Leukocytes', 'Basophils', 'MCH', 'Eosinophils', 'MCV', 'Monocytes', 'RDW'} elif required_cols_14.issubset(data.columns): data['Prediction'] = model14.predict(data) out = 2
5. Safe Directory Creation
Using os.mkdir('uploads') will throw an error if the directory already exists. Replace it with os.makedirs which safely creates the directory only if it doesn’t exist:
os.makedirs('uploads', exist_ok=True)
Corrected Full Code
Here’s the fixed version of your core Flask code:
import os import secrets import pandas as pd from flask import Flask, request, redirect, url_for, render_template, send_from_directory app = Flask(__name__) # Assume model4 and model14 are loaded here def predict(path): data = pd.read_csv(path) filename = os.path.basename(path) pathout = os.path.join('uploads', f"{filename}_out.csv") required_cols_4 = {'Leukocytes', 'Platelets', 'Eosinophils', 'Monocytes'} if required_cols_4.issubset(data.columns): data['Prediction'] = model4.predict(data) out = 1 elif {'Hematocrit', 'Hemoglobin', 'Platelets', 'MPV', 'Erythrocytes', 'Lymphocytes', 'MCHC', 'Leukocytes', 'Basophils', 'MCH', 'Eosinophils', 'MCV', 'Monocytes', 'RDW'}.issubset(data.columns): data['Prediction'] = model14.predict(data) out = 2 else: out = 3 data.to_csv(pathout, index=False) # Avoid extra index column in output return out, pathout @app.route("/csvIn", methods=['GET', 'POST']) def csvInput(): if request.method == 'POST': csv = request.files['file'] os.makedirs('uploads', exist_ok=True) filename = secrets.token_hex(8) path = os.path.join('uploads', filename) csv.save(path) out, pathout = predict(path) return redirect(url_for('csvOut', out=out, pathout=pathout)) return render_template("csv.html") @app.route("/csvOut", methods=['GET']) def csvOut(): pathout = request.args.get("pathout") out = request.args.get("out") if out in ('1', '2'): # request.args returns strings, not integers result = "Please find the predicted output in the column titled 'Prediction' in the file attached below" else: result = "The uploaded csv file does not match the provided template. Please recheck the CSV file below and upload again. \n If the problem persists, please contact the admin teams for further clarification." download_filename = os.path.basename(pathout) return render_template("out.html", download_filename=download_filename, result=result) @app.route('/uploads/<filename>') def uploaded_file(filename): return send_from_directory('uploads', filename) if __name__ == '__main__': app.run(debug=True)
And update your out.html to use the new download variable:
<head> <title>Batch Prediction</title> </head> <body> <div> <h2><b> Result: {{ result }} </b></h2><br> <p>If the file format is correct, the model predictions have been appended to the dataset in the form of a feature titled "Prediction".</p><br> <a href="{{ url_for('uploaded_file', filename=download_filename) }}" download><button>Download File</button></a><br> <a href="/home"><button class="btnbl">Return Home</button></a> </div> </body>
Quick Test Tips
- Enable
debug=Trueto see detailed error messages if something goes wrong. - Ensure
model4andmodel14are properly loaded before running the app. - The
index=Falseflag into_csvkeeps the output file clean by avoiding an extra index column.
内容的提问来源于stack exchange,提问作者Ali Safdari

