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)
原因分析
- 响应体积触发的隐式异常:当缩放系数改为255时,生成的字节数据量可能远大于之前的测试数据(
np.ones((100,123))仅12300字节,而Nifti图像多为三维结构,数据量会暴增)。Flask在处理超大响应时可能出现内部错误,导致CORS中间件未能正确注入响应头。 - numpy类型转换潜在问题:虽然理论上
(data-datamin)/(datamax-datamin)*255的结果不会超过255,但如果datamax和datamin相等(图像所有像素值相同),会触发除以0的计算错误,导致后续字节生成异常,进而干扰Flask的响应处理流程。 - 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
相关产品推荐
相关产品推荐

