如何使用Flask将动态生成的PNG图片发送至浏览器并在JavaScript Canvas中展示
现有代码问题定位
- 前端对象URL使用逻辑错误:
URL.createObjectURL仅支持接收Blob/File类型的二进制数据,直接传入base64字符串会生成无效的空链接,图片根本加载不出来 - 后端base64返回格式不完整:裸base64编码串缺少
data:image/png;base64,的Data URI前缀,浏览器无法识别为可加载的图片资源 - DOM引用错配:前端代码获取2d上下文时用的是
canvasFinalConsensusMask,但HTML里实际的canvas元素ID是myCanvas,运行时会直接报空值引用错误 - 多余的磁盘IO操作:实时生成的图像处理完成后完全可以在内存里完成编码,不需要特意落盘存为本地PNG文件再读取,平白增加IO开销
- 缺少必要依赖导入:代码里用到的
Image类来自PIL库,原代码没有对应的import语句,直接运行会触发NameError
修正实现方案
方案1:最小改动兼容现有base64流程
适合不想大改现有编码逻辑的场景,仅调整前后端的参数传递格式
后端代码修正
from flask import Flask, jsonify from PIL import Image # 补充缺失的PIL导入 import base64 import io app = Flask(__name__) @app.route('/getImage', methods=['GET']) def get_Image(): # 保留原有cv2图像处理逻辑,处理完成后直接在内存中转字节流 img_byte_arr = io.BytesIO() # 实时生成的图片直接从内存读取,无需调用本地磁盘文件 img = Image.open("img\\truthMask.png") img.save(img_byte_arr, format='PNG') img_byte_arr.seek(0) # 移动流指针到起始位置,避免读取到空字节 # 编码base64并补全Data URI前缀 encoded_img = base64.b64encode(img_byte_arr.getvalue()).decode('ascii') full_img_src = f"data:image/png;base64,{encoded_img}" return jsonify({'image_url': full_img_src})
前端代码修正
// 对齐实际DOM的ID,替换原来错误的canvas引用 const canvasMask = document.getElementById('myCanvas'); const ctxcanvasMask = canvasMask.getContext('2d'); document.getElementById("getMask").addEventListener('click', () => { const xhr = new XMLHttpRequest(); xhr.onreadystatechange = function () { if (xhr.readyState !== 4) return; if (xhr.status >= 200 && xhr.status < 300) { const res = JSON.parse(xhr.responseText); const img = new Image(); img.onload = function () { // 动态匹配canvas尺寸,避免固定100x100的画布显示不全 canvasMask.width = img.width; canvasMask.height = img.height; ctxcanvasMask.drawImage(img, 0, 0); }; // 直接把带Data头的base64串作为img源,不需要转对象URL img.src = res.image_url; } }; xhr.open('GET', 'http://127.0.0.1:5000/getImage'); xhr.send(); });
方案2:二进制直返(推荐生产场景使用)
base64编码会让图片体积增加约33%,实时生成高分辨率Mask图时传输效率较低,直接返回内存中的二进制字节流,整体开销更小。
后端代码修正
from flask import Flask, send_file from PIL import Image import io app = Flask(__name__) @app.route('/getImage', methods=['GET']) def get_Image(): # 原有cv2图像处理逻辑,生成完的图直接写入内存流 img_byte_arr = io.BytesIO() img = Image.open("img\\truthMask.png") img.save(img_byte_arr, format='PNG') img_byte_arr.seek(0) # 直接返回内存流,指定响应类型为PNG图片 return send_file( img_byte_arr, mimetype='image/png' )
前端代码修正
const canvasMask = document.getElementById('myCanvas'); const ctx = canvasMask.getContext('2d'); document.getElementById("getMask").addEventListener('click', () => { const xhr = new XMLHttpRequest(); // 指定响应类型为Blob,才能拿到原始二进制数据 xhr.responseType = 'blob'; xhr.onreadystatechange = function () { if (xhr.readyState !== 4) return; if (xhr.status >= 200 && xhr.status < 300) { const imgBlob = xhr.response; const imgUrl = URL.createObjectURL(imgBlob); const img = new Image(); img.onload = function () { canvasMask.width = img.width; canvasMask.height = img.height; ctx.drawImage(img, 0, 0); // 图片加载完成后释放对象URL,避免内存泄漏 URL.revokeObjectURL(imgUrl); }; img.src = imgUrl; } }; xhr.open('GET', 'http://127.0.0.1:5000/getImage'); xhr.send(); });
常见注意点
- 跨域场景下需要在Flask端配置CORS规则,否则浏览器会拦截XHR请求
- 内存流操作完成后记得调用
seek(0)重置指针,否则返回的内容会是空的 - 不要对实时生成的图做多余的落盘存储操作,全程在内存中处理即可减少不必要的延迟
内容的提问来源于stack exchange,提问作者boardkeystown
相关产品推荐
相关产品推荐

