Azure Web App中Streamlit应用下载PPT触发AssertionError求助
问题分析
这个AssertionError: session with id 'xxx' is already connected!是Streamlit部署在Azure Web App时,WebSocket会话重复连接引发的异常,结合你的场景,触发核心原因有两点:
- 主线程长时间阻塞:点击Generate后,
subprocess.run同步执行R脚本,Streamlit主线程被持续占用,导致和浏览器的WebSocket连接超时,浏览器自动发起重连,引发会话冲突。 - 会话状态未持久化:
binary_appendix仅在Generate按钮的分支中定义,页面重新渲染(比如点击Download时)变量会丢失,加上特定季度生成的PPT数据量更大,进一步放大了会话状态异常问题。
规避与解决方案
1. 异步执行R脚本,避免主线程阻塞
将同步执行的R脚本改成异步线程执行,防止Streamlit主线程卡住导致WebSocket超时:
import subprocess import threading import time import pandas as pd from io import BytesIO with col1: result = st.button("Generate") if result: t1 = pd.Timestamp.now() st.write(f'Started at {str(pd.Timestamp.now().time())}') # 定义异步执行的函数 def run_r_scripts(quarter): subprocess.run(["Rscript", "appendix_images_fcn.R", quarter]) subprocess.run(["Rscript", "pla_fcn.R", quarter]) prs = appendix_fcn.create_appendix(quarter) binary_appendix = BytesIO() prs.save(binary_appendix) # 将生成结果存入会话状态 st.session_state['binary_appendix'] = binary_appendix st.session_state['generation_time'] = str(pd.Timestamp.now()-t1) # 启动后台线程执行 threading.Thread(target=run_r_scripts, args=(quarter,), daemon=True).start() # 显示加载状态,等待生成完成 with st.spinner("Generating PPT..."): while 'binary_appendix' not in st.session_state: time.sleep(0.5) st.write(f'generated in {st.session_state["generation_time"]}')
2. 用会话状态持久化二进制数据
把生成的PPT二进制数据存入st.session_state,避免页面重新渲染时变量丢失:
with col2: # 仅当生成完成后才显示下载按钮 if 'binary_appendix' in st.session_state: current_time = str(pd.Timestamp.now())[2:-7].replace('-','').replace(' ','_').replace(':','') st.download_button( label='Download', data=st.session_state['binary_appendix'].getvalue(), file_name=f'Appendix_{quarter}_{current_time}.pptx' ) else: st.info("请先点击'Generate'生成PPT后再下载")
3. 调整Azure Web App与Streamlit配置
- 在Azure Portal进入你的Web App,在配置-应用程序设置中,将
WEBSOCKET_ENABLED设为On,确保WebSocket连接正常。 - 提升Web App的实例内存与CPU配额,针对数据量较大的季度,避免资源不足导致进程阻塞。
- 在项目根目录的
.streamlit/config.toml中添加WebSocket相关配置,增大消息大小限制:
[server] enableWebsocketCompression = true websocketMaxMessageSize = 52428800 # 50MB,可根据实际PPT大小调整
4. 额外优化:数据量预检查
针对特定季度数据过大的情况,在生成前添加数据规模检查,提前预警:
if result: # 自定义检查逻辑,比如读取该季度相关文件的大小 def check_quarter_data_size(quarter): # 示例:读取生成所需的数据源文件大小 import os data_path = f"data_{quarter}.csv" return os.path.getsize(data_path) if os.path.exists(data_path) else 0 data_size = check_quarter_data_size(quarter) if data_size > 100 * 1024 * 1024: # 100MB阈值 st.warning("该季度数据量较大,生成过程可能耗时较长,请耐心等待") # 后续生成逻辑...
内容的提问来源于stack exchange,提问作者frank
相关产品推荐
相关产品推荐

