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

如何使用Python Plotly Dash实现Web应用上传图片的本地存储

Dash 上传图片本地存储+全流程实现方案

Dash 的 dcc.Upload 组件默认返回带格式头的base64编码内容,不需要额外引入第三方上传依赖,解码后即可直接写入本地文件夹,整套实现逻辑如下:

前置准备

  • 项目启动时自动创建两类存储文件夹,分别存放原始上传图、处理后结果图,避免路径不存在触发报错
  • 给应用配置单文件上传大小上限,避免超大文件拖垮服务
  • 上传组件限制可选文件类型为常见图片格式,减少无效上传
import os
import io
import base64
import time
from dash import Dash, dcc, html, Input, Output, State
from PIL import Image

# 初始化存储路径
ORIGIN_IMG_PATH = "uploaded_originals"
PROCESSED_IMG_PATH = "processed_results"
os.makedirs(ORIGIN_IMG_PATH, exist_ok=True)
os.makedirs(PROCESSED_IMG_PATH, exist_ok=True)

app = Dash(__name__)
# 限制单张上传图最大10M
app.server.config["MAX_CONTENT_LENGTH"] = 10 * 1024 * 1024

# 页面布局
app.layout = html.Div([
    dcc.Upload(
        id="upload-img",
        children=html.Div("点击或拖拽图片到此处上传"),
        accept="image/*",
        style={
            "width": "50%",
            "height": "80px",
            "lineHeight": "80px",
            "borderWidth": "1px",
            "borderStyle": "dashed",
            "borderRadius": "5px",
            "textAlign": "center",
            "margin": "20px 0"
        }
    ),
    html.Div(id="img-show-area")
])

核心存储+处理回调逻辑

注意dcc.Upload返回的contents字段格式为data:image/[格式];base64,[实际编码串],必须先按逗号拆分去掉格式头,再解码base64内容,否则存出来的文件会损坏打不开。

@app.callback(
    Output("img-show-area", "children"),
    Input("upload-img", "contents"),
    State("upload-img", "filename"),
    prevent_initial_call=True
)
def save_and_process_img(contents, filename):
    if not contents:
        return html.Div("未检测到有效上传文件")
    
    # 拆分格式头和真实编码内容
    content_header, img_base64 = contents.split(",", 1)
    # 解码得到图片二进制
    img_binary = base64.b64decode(img_base64)

    # 生成带时间戳的唯一文件名,避免同名文件覆盖
    file_suffix = os.path.splitext(filename)[1]
    unique_filename = f"{int(time.time())}{file_suffix}"
    # 写入本地原始文件夹
    origin_save_fullpath = os.path.join(ORIGIN_IMG_PATH, unique_filename)
    with open(origin_save_fullpath, "wb") as f:
        f.write(img_binary)

    # --------------------------
    # 此处替换为你自己的图片处理逻辑
    processed_save_fullpath = os.path.join(PROCESSED_IMG_PATH, f"processed_{unique_filename}")
    with Image.open(io.BytesIO(img_binary)) as im:
        # 示例:转灰度图,替换成你需要的处理逻辑即可
        processed_im = im.convert("L")
        processed_im.save(processed_save_fullpath)
    # --------------------------

    # 读取处理后的图转base64用于前端展示,开发阶段不需要配静态路由
    with open(processed_save_fullpath, "rb") as f:
        processed_base64 = base64.b64encode(f.read()).decode()
    processed_src = f"{content_header},{processed_base64}"

    # 同时返回原图和处理后图
    return html.Div([
        html.Div([
            html.H5("原始图片"),
            html.Img(src=contents, style={"maxWidth": "45%", "margin": "10px"})
        ]),
        html.Div([
            html.H5("处理后图片"),
            html.Img(src=processed_src, style={"maxWidth": "45%", "margin": "10px"})
        ])
    ])

if __name__ == "__main__":
    app.run_server(debug=True)

实践避坑提醒

  • 不要直接把本地文件绝对路径塞到html.Img的src属性里,浏览器没有权限直接读取服务器本地文件,开发阶段直接把图片转base64传入src最省事,正式上线再配置静态资源路由可以降低带宽占用
  • 文件名必须加唯一标识(时间戳、随机字符串都可以),否则不同用户上传同名文件会互相覆盖,多用户场景建议再加一层用户ID前缀
  • 不要只靠前端accept属性做文件格式校验,后端也要判断文件后缀是否在允许的图片格式列表内,避免恶意用户上传可执行脚本
  • Linux环境部署时,记得给两个图片存储文件夹开放服务进程的读写权限,否则会触发权限错误导致文件存储失败

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.01 22:21:37