React与Django交互:CSV文件上传、处理及结果返回方案咨询
React + Django 实现CSV无存储式处理方案
前端React实现(文件上传与结果展示)
用FormData直接传输CSV文件,无需提前序列化,适配Django的文件接收逻辑:
import { useState } from 'react'; function CSVUploader() { const [selectedFile, setSelectedFile] = useState(null); const [results, setResults] = useState(null); const [errorMsg, setErrorMsg] = useState(''); const handleFilePick = (e) => { const file = e.target.files[0]; if (file && file.type === 'text/csv') { setSelectedFile(file); setErrorMsg(''); setResults(null); } else { setErrorMsg('请选择有效的CSV文件'); } }; const handleSubmit = async (e) => { e.preventDefault(); if (!selectedFile) return; const formData = new FormData(); formData.append('csv_file', selectedFile); try { const res = await fetch('/api/process-csv/', { method: 'POST', headers: { 'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value, }, body: formData, }); if (!res.ok) throw new Error('服务器处理失败'); const data = await res.json(); setResults(data); } catch (err) { setErrorMsg(err.message); } }; return ( <div className="upload-container"> <form onSubmit={handleSubmit}> <input type="file" accept=".csv" onChange={handleFilePick} /> <button type="submit" disabled={!selectedFile}>上传处理</button> </form> {errorMsg && <p className="error">{errorMsg}</p>} {results && ( <div className="results"> <h3>处理结果</h3> <div className="model-info"> <p>模型系数:{results.model_coef.join(', ')}</p> <p>截距:{results.model_intercept}</p> </div> <img src={`data:image/png;base64,${results.chart_base64}`} alt="预测趋势图" /> <a href={`/api/generate-pdf/?task_id=${results.task_id}`} target="_blank" rel="noopener noreferrer"> 下载PDF报告 </a> </div> )} </div> ); } export default CSVUploader;
后端Django实现(文件接收与内存处理)
不存储文件到数据库,直接在内存中解析CSV,处理完成后返回结果:
1. 视图逻辑
from django.views import View from django.http import JsonResponse, HttpResponse from django.conf import settings import pandas as pd import io import base64 import matplotlib.pyplot as plt from sklearn.linear_model import LinearRegression import uuid import os from reportlab.pdfgen import canvas from reportlab.lib.pagesizes import letter class CSVProcessView(View): def post(self, request): # 校验文件是否存在 if 'csv_file' not in request.FILES: return JsonResponse({'error': '未上传CSV文件'}, status=400) csv_file = request.FILES['csv_file'] # 内存中读取CSV,避免写入数据库 try: df = pd.read_csv(io.StringIO(csv_file.read().decode('utf-8'))) except Exception as e: return JsonResponse({'error': f'CSV解析失败: {str(e)}'}, status=400) # 替换为你的数据挖掘算法(示例用线性回归) required_cols = ['feature', 'target'] if not all(col in df.columns for col in required_cols): return JsonResponse({'error': 'CSV缺少特征/目标列'}, status=400) X = df[['feature']] y = df['target'] model = LinearRegression() model.fit(X, y) predictions = model.predict(X).tolist() # 生成图表并转base64返回前端 plt.figure(figsize=(8, 5)) plt.scatter(df['feature'], df['target'], label='原始数据') plt.plot(df['feature'], predictions, color='red', label='预测曲线') plt.legend() buffer = io.BytesIO() plt.savefig(buffer, format='png', bbox_inches='tight') buffer.seek(0) chart_base64 = base64.b64encode(buffer.getvalue()).decode('utf-8') plt.close() # 生成临时任务ID,用于关联后续PDF生成的临时数据 task_id = str(uuid.uuid4()) temp_dir = os.path.join(settings.BASE_DIR, 'temp_csv_data') os.makedirs(temp_dir, exist_ok=True) # 临时保存处理后的数据(生成PDF时用) df['predictions'] = predictions df.to_csv(os.path.join(temp_dir, f'{task_id}.csv'), index=False) return JsonResponse({ 'predictions': predictions, 'model_coef': model.coef_.tolist(), 'model_intercept': float(model.intercept_), 'chart_base64': chart_base64, 'task_id': task_id }) class PDFGenerateView(View): def get(self, request): task_id = request.GET.get('task_id') if not task_id: return JsonResponse({'error': '缺少任务ID'}, status=400) temp_file_path = os.path.join(settings.BASE_DIR, 'temp_csv_data', f'{task_id}.csv') if not os.path.exists(temp_file_path): return JsonResponse({'error': '数据已过期'}, status=404) df = pd.read_csv(temp_file_path) # 生成PDF内容 buffer = io.BytesIO() p = canvas.Canvas(buffer, pagesize=letter) width, height = letter # 写入标题与模型信息 p.setFont("Helvetica-Bold", 16) p.drawString(100, height-50, '数据挖掘分析报告') p.setFont("Helvetica", 12) p.drawString(100, height-80, f'模型系数: {df["predictions"].iloc[0]:.2f}') p.drawString(100, height-100, f'截距: {df["predictions"].iloc[0]:.2f}') # 写入数据表格(简化版,可使用ReportLab的Table组件优化) y_pos = height - 130 p.drawString(100, y_pos, '特征值 | 实际值 | 预测值') y_pos -= 20 for _, row in df.iterrows(): p.drawString(100, y_pos, f'{row["feature"]:.2f} | {row["target"]:.2f} | {row["predictions"]:.2f}') y_pos -= 20 if y_pos < 50: p.showPage() y_pos = height - 50 p.save() buffer.seek(0) # 返回PDF文件 response = HttpResponse(buffer, content_type='application/pdf') response['Content-Disposition'] = f'attachment; filename="report_{task_id}.pdf"' # 清理临时文件 os.remove(temp_file_path) return response
2. URL配置
from django.urls import path from .views import CSVProcessView, PDFGenerateView urlpatterns = [ path('api/process-csv/', CSVProcessView.as_view(), name='process_csv'), path('api/generate-pdf/', PDFGenerateView.as_view(), name='generate_pdf'), ]
关键注意事项
- 依赖安装:执行
pip install pandas matplotlib scikit-learn reportlab安装所需库 - 临时文件清理:可添加定时任务(如Celery)定期删除
temp_csv_data目录下的过期文件 - 文件大小限制:在Django的
settings.py中设置FILE_UPLOAD_MAX_MEMORY_SIZE控制可上传的最大文件大小 - 前后端分离场景:若React部署在独立域名,需在Django的
settings.py中配置CSRF_TRUSTED_ORIGINS并让前端携带正确的CSRF Token
内容的提问来源于stack exchange,提问作者user20627094
相关产品推荐
相关产品推荐

