Docker容器中Python Dash dcc.Upload组件访问权限异常求助
问题解答:Docker中Dash的dcc.Upload组件为何能访问宿主机文件?
核心误解澄清
你对dcc.Upload和Docker的作用范围存在关键混淆:
- dcc.Upload是浏览器端组件:它的文件选择对话框运行在用户操作的本地浏览器中,而非Docker容器内部。用户选择的是自己本地机器(也就是你所说的"宿主机")的文件,随后文件内容会通过HTTP请求上传到容器内的Dash应用。整个过程中,组件从未直接访问容器或宿主机的文件系统,只是把用户本地文件的内容传送给服务器。
- Docker的隔离性是容器与宿主机之间的隔离:容器内的应用无法直接访问宿主机文件系统(除非挂载了卷),但这和dcc.Upload的行为无关——因为文件选择的环节完全在浏览器端完成。
如何实现"仅访问容器内文件"的需求
如果你希望应用只能读取容器内部的文件(或挂载的指定卷),需要修改应用逻辑,不再依赖浏览器端的文件选择,而是让应用直接读取容器内的文件路径,或者提供容器内文件的列表供用户选择。以下是修改后的示例:
修改后的Dash应用代码
from dash import Dash, dcc, html, dash_table, Input, Output, State, callback import pandas as pd import os external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css'] app = Dash(__name__, external_stylesheets=external_stylesheets) # 定义容器内要访问的目录 TARGET_DIR = "/code/upload_files" # 确保目录存在 os.makedirs(TARGET_DIR, exist_ok=True) app.layout = html.Div([ # 下拉框展示容器内指定目录的文件 dcc.Dropdown( id='container-file-select', options=[], placeholder="选择容器内的文件", style={'width': '80%', 'margin': '10px'} ), html.Div(id='output-container-file'), ]) @callback( Output('container-file-select', 'options'), Input('container-file-select', 'value') ) def update_file_list(_): # 获取目标目录下的文件列表 files = [f for f in os.listdir(TARGET_DIR) if os.path.isfile(os.path.join(TARGET_DIR, f))] return [{'label': f, 'value': f} for f in files] @callback( Output('output-container-file', 'children'), Input('container-file-select', 'value') ) def display_file_content(selected_file): if not selected_file: return html.Div("请选择一个文件") file_path = os.path.join(TARGET_DIR, selected_file) try: if selected_file.endswith('.csv'): df = pd.read_csv(file_path) elif selected_file.endswith('.xls') or selected_file.endswith('.xlsx'): df = pd.read_excel(file_path) else: return html.Div("仅支持CSV和Excel文件") return html.Div([ html.H5(f"容器内文件:{selected_file}"), dash_table.DataTable( df.to_dict('records'), [{'name': i, 'id': i} for i in df.columns] ) ]) except Exception as e: return html.Div(f"读取文件出错:{str(e)}") if __name__ == '__main__': app.run(host='0.0.0.0', port=8030, debug=True)
调整Docker配置(可选:挂载卷)
如果你想让容器访问宿主机的指定目录(而非仅容器内部),可以在运行容器时挂载卷:
docker run -d --name ContainerName -p 8030:8030 -v /宿主机指定目录:/code/upload_files ImageName
这样容器内的/code/upload_files目录就会映射到宿主机的指定目录,应用就能读取该目录下的文件,同时不会访问宿主机其他位置。
原问题中的代码参考
原Dash应用代码
from dash import Dash, dcc, html, dash_table, Input, Output, State, callback import base64 import datetime import io import pandas as pd external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css'] app = Dash(__name__, external_stylesheets=external_stylesheets) app.layout = html.Div([ dcc.Upload( id='upload-data', children=html.Div([ 'Drag and Drop or ', html.A('Select Files') ]), style={ 'width': '100%', 'height': '60px', 'lineHeight': '60px', 'borderWidth': '1px', 'borderStyle': 'dashed', 'borderRadius': '5px', 'textAlign': 'center', 'margin': '10px' }, # Allow multiple files to be uploaded multiple=True ), html.Div(id='output-data-upload'), ]) def parse_contents(contents, filename, date): content_type, content_string = contents.split(',') decoded = base64.b64decode(content_string) try: if 'csv' in filename: # Assume that the user uploaded a CSV file df = pd.read_csv( io.StringIO(decoded.decode('utf-8'))) elif 'xls' in filename: # Assume that the user uploaded an excel file df = pd.read_excel(io.BytesIO(decoded)) except Exception as e: print(e) return html.Div([ 'There was an error processing this file.' ]) return html.Div([ html.H5(filename), html.H6(datetime.datetime.fromtimestamp(date)), dash_table.DataTable( df.to_dict('records'), [{'name': i, 'id': i} for i in df.columns] ), html.Hr(), # horizontal line # For debugging, display the raw contents provided by the web browser html.Div('Raw Content'), html.Pre(contents[0:200] + '...', style={ 'whiteSpace': 'pre-wrap', 'wordBreak': 'break-all' }) ]) @callback(Output('output-data-upload', 'children'), Input('upload-data', 'contents'), State('upload-data', 'filename'), State('upload-data', 'last_modified')) def update_output(list_of_contents, list_of_names, list_of_dates): if list_of_contents is not None: children = [ parse_contents(c, n, d) for c, n, d in zip(list_of_contents, list_of_names, list_of_dates)] return children if __name__ == '__main__': app.run(host='0.0.0.0', port=8030, debug=True)
原Dockerfile
FROM python:3 ENV PYTHONUNBUFFERED 1 RUN mkdir /code WORKDIR /code Copy app_3.py /code COPY requirements.txt /code/ RUN pip install -r requirements.txt COPY . /code/ CMD python app_3.py
原容器运行命令
docker build -t ImageName . docker run -d --name ContainerName -p 8030:8030 ImageName
内容的提问来源于stack exchange,提问作者Marlon0260
相关产品推荐
相关产品推荐

