如何用FastAPI+Nextjs不本地保存显示Matplotlib以太坊余额图表?
Next.js + FastAPI 以太坊余额图表生成报错(Internal Server Error)
我用Next.js搭建前端、FastAPI搭建后端开发网站,前端设有以太坊地址输入表单,后端根据输入地址生成「以太坊余额随时间变化」的Matplotlib图表,要求不本地保存图表,直接通过FastAPI返回给前端。目前访问localhost:8000/images时出现「Internal Server Error」,无法正常显示图表,相关代码如下:
前端Next.js代码(Chart.tsx)
fetch("http://localhost:8000/image", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(ethAddress), }).then(fetchEthAddresses);
后端图表生成代码(ethBalanceTracker.py)
# Imports # Logic for chart here plt.plot(times, balances) buf = BytesIO() plt.savefig(buf, format="png") buf.seek(0) return StreamingResponse(buf, media_type="image/png")
后端FastAPI代码(api.py)
@app.get("/image") async def get_images() -> dict: return {"data": images} @app.post("/image") async def add_image(ethAddress: dict) -> dict: test = EthBalanceTracker.get_transactions(ethAddress["ethAddress"]) images.append(test)
问题分析
- API逻辑错误:POST接口将
StreamingResponse对象存入images数组后未返回有效响应,且GET接口返回的是包含响应对象的数组,无法直接作为图片资源解析。 - Matplotlib环境问题:非交互式环境下未指定渲染后端,可能触发显示相关报错。
- 前端请求逻辑错误:POST请求后未直接处理返回的图片流,而是调用GET接口获取无效数据。
修复方案
1. 修正FastAPI接口逻辑
删除无用的GET接口,让POST接口直接返回生成的图表流:
# api.py from fastapi import FastAPI, Request from fastapi.responses import StreamingResponse from ethBalanceTracker import EthBalanceTracker app = FastAPI() @app.post("/image") async def generate_image(request: Request): data = await request.json() eth_address = data.get("ethAddress") return EthBalanceTracker.get_transactions(eth_address)
2. 配置Matplotlib非交互式后端
在ethBalanceTracker.py开头添加后端配置,并释放图表资源:
import matplotlib matplotlib.use('Agg') # 指定非交互式渲染后端 import matplotlib.pyplot as plt from io import BytesIO from fastapi.responses import StreamingResponse # 实现get_transactions方法 def get_transactions(eth_address): # 这里补充获取times和balances的业务逻辑 times = [] # 替换为你的时间数据 balances = [] # 替换为你的余额数据 plt.plot(times, balances) buf = BytesIO() plt.savefig(buf, format="png") buf.seek(0) plt.close() # 关闭图表,释放内存资源 return StreamingResponse(buf, media_type="image/png")
3. 调整前端请求处理逻辑
直接处理POST返回的图片流,生成可显示的URL:
// Chart.tsx import { useState } from 'react'; export default function Chart() { const [ethAddress, setEthAddress] = useState(''); const [chartImageUrl, setChartImageUrl] = useState(''); const handleGenerateChart = async () => { try { const response = await fetch("http://localhost:8000/image", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ ethAddress }), // 确保发送的是包含字段的对象 }); if (!response.ok) throw new Error('生成图表失败'); const blob = await response.blob(); const imageUrl = URL.createObjectURL(blob); setChartImageUrl(imageUrl); } catch (err) { console.error(err); } }; return ( <div> <input type="text" value={ethAddress} onChange={(e) => setEthAddress(e.target.value)} placeholder="输入以太坊地址" /> <button onClick={handleGenerateChart}>生成余额图表</button> {chartImageUrl && <img src={chartImageUrl} alt="以太坊余额变化图表" />} </div> ); }
内容的提问来源于stack exchange,提问作者spal
相关产品推荐
相关产品推荐

