You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.26 01:15:07