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

numpy数组幅值为何影响Flask的CORS?求排查与解决方案

奇怪的CORS问题:修改ndarray幅值触发跨域错误

初始正常场景

Flask后端代码(app.py)

## app.py

from flask import Flask
from flask_cors import CORS
import numpy as np
import nibabel as nib

app = Flask(__name__)
CORS(app)

@app.route("/",methods=["POST"])
def readNifti():
    img = nib.load("../data/test_project/sub-mrmdCrown_Hep3bLuc_11/ses-iv03/anat/sub-mrmdCrown_Hep3bLuc_11_ses-iv03_acq-TurboRARE_run-5_T2w.nii.gz")
    data = img.get_fdata()
    datamin=data.min()
    datamax=data.max()
    data = np.round((data-datamin)/(datamax-datamin)*200).astype(np.uint8).tobytes()
    # return data
    return np.ones((100,123),dtype=np.uint8).tobytes()

app.run(port=3413)

Vue前端代码

<template>
  <div>
    Hello World
  </div>
</template>

<script setup lang="ts">
import { onMounted } from 'vue';

onMounted(() => {
  fetch("http://localhost:3413", {
    method: "POST"
  })
    .then(res => res.arrayBuffer())
    .then((data) => {
      console.log(data.byteLength);
    })
})
</script>

此时Vue运行在3412端口,Flask在3413端口,跨域请求正常工作。

第一次修改:返回处理后的Nifti数据

仅修改app.py的返回行,替换为实际处理后的Nifti字节数据,仍然正常运行:

@app.route("/",methods=["POST"])
def readNifti():
    img = nib.load("../data/test_project/sub-mrmdCrown_Hep3bLuc_11/ses-iv03/anat/sub-mrmdCrown_Hep3bLuc_11_ses-iv03_acq-TurboRARE_run-5_T2w.nii.gz")
    data = img.get_fdata()
    datamin=data.min()
    datamax=data.max()
    data = np.round((data-datamin)/(datamax-datamin)*200).astype(np.uint8).tobytes()
    return data # 仅修改此行
    return np.ones((100,123),dtype=np.uint8).tobytes()

第二次修改:调整幅值到255,触发CORS错误

仅将缩放系数从200改为255,前端代码无任何改动,但浏览器抛出跨域错误:

@app.route("/",methods=["POST"])
def readNifti():
    img = nib.load("../data/test_project/sub-mrmdCrown_Hep3bLuc_11/ses-iv03/anat/sub-mrmdCrown_Hep3bLuc_11_ses-iv03_acq-TurboRARE_run-5_T2w.nii.gz")
    data = img.get_fdata()
    datamin=data.min()
    datamax=data.max()
    # 下一行:200 -> 255,仅修改此数值
    data = np.round((data-datamin)/(datamax-datamin)*255).astype(np.uint8).tobytes()
    return data
    return np.ones((100,123),dtype=np.uint8).tobytes()

浏览器错误信息

Access to fetch at 'http://localhost:3413/' from origin 'http://localhost:3412' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

背景信息

  • 使用SSH本地端口转发,将Linux主机的3413/3412端口转发到Windows机器
  • 每次修改Python代码后已手动重启Flask应用(排除自动重启不可靠的问题)

版本信息

Chrome: Version 114.0.5735.199 (Official Build) (64-bit)

>>> import flask
>>> flask.__version__
'2.2.2'
>>> import flask_cors
>>> flask_cors.__version__
'3.0.10'
>>> import numpy
>>> numpy.__version__
'1.25.0'
>>> import nibabel
>>> nibabel.__version__
'5.1.0'
$ node -v
v16.13.1
$ npm -v
8.1.2
$ cat /etc/redhat-release
CentOS Linux release 7.9.2009 (Core)

原因分析

  1. 响应体积触发的隐式异常:当缩放系数改为255时,生成的字节数据量可能远大于之前的测试数据(np.ones((100,123))仅12300字节,而Nifti图像多为三维结构,数据量会暴增)。Flask在处理超大响应时可能出现内部错误,导致CORS中间件未能正确注入响应头。
  2. numpy类型转换潜在问题:虽然理论上(data-datamin)/(datamax-datamin)*255的结果不会超过255,但如果datamax和datamin相等(图像所有像素值相同),会触发除以0的计算错误,导致后续字节生成异常,进而干扰Flask的响应处理流程。
  3. SSH端口转发限制:大体积响应在通过SSH端口转发时可能被截断,导致响应不完整,CORS头丢失。

解决方法

1. 手动添加CORS响应头

不依赖全局CORS(app)配置,在路由中显式设置响应头,确保无论响应大小都能正确返回:

from flask import make_response

@app.route("/",methods=["POST"])
def readNifti():
    img = nib.load("../data/test_project/sub-mrmdCrown_Hep3bLuc_11/ses-iv03/anat/sub-mrmdCrown_Hep3bLuc_11_ses-iv03_acq-TurboRARE_run-5_T2w.nii.gz")
    data = img.get_fdata()
    datamin=data.min()
    datamax=data.max()
    
    if datamax == datamin:
        data = np.zeros_like(data, dtype=np.uint8).tobytes()
    else:
        data = np.round((data-datamin)/(datamax-datamin)*255).astype(np.uint8).tobytes()
    
    response = make_response(data)
    response.headers["Access-Control-Allow-Origin"] = "http://localhost:3412"
    response.headers["Access-Control-Allow-Methods"] = "POST"
    return response

2. 分块传输大响应

如果数据量确实过大,使用Flask的Response实现分块传输,避免一次性生成巨量字节导致内存或响应处理异常:

from flask import Response

@app.route("/",methods=["POST"])
def readNifti():
    img = nib.load("../data/test_project/sub-mrmdCrown_Hep3bLuc_11/ses-iv03/anat/sub-mrmdCrown_Hep3bLuc_11_ses-iv03_acq-TurboRARE_run-5_T2w.nii.gz")
    data = img.get_fdata()
    datamin=data.min()
    datamax=data.max()
    
    if datamax == datamin:
        byte_data = np.zeros_like(data, dtype=np.uint8).tobytes()
    else:
        byte_data = np.round((data-datamin)/(datamax-datamin)*255).astype(np.uint8).tobytes()
    
    def generate_chunks():
        chunk_size = 1024 * 1024  # 按1MB分块
        for i in range(0, len(byte_data), chunk_size):
            yield byte_data[i:i+chunk_size]
    
    response = Response(generate_chunks(), mimetype="application/octet-stream")
    response.headers["Access-Control-Allow-Origin"] = "http://localhost:3412"
    return response

3. 排除SSH端口转发干扰

直接在Linux主机上启动前后端并测试请求,确认是否是端口转发导致的响应截断问题。如果是,调整SSH转发参数(如增大缓冲区)或改用其他转发方式。


内容的提问来源于stack exchange,提问作者Septem

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 03:25:08