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

使用Material-UI与ReactJS无法从下拉菜单下载Excel文件

React + Material-UI 文件下载失败问题修复

问题描述

我尝试从public目录(路径为public/data/User.xlsx)下载文件,但无法成功完成下载。当前使用Material-UI和React.js开发该功能,相关component.js代码如下:

import * as React from 'react'
import { Button, Menu, MenuItem } from '@mui/material'
import isEmpty from 'lodash/fp/isEmpty'
import { styled, alpha } from '@mui/material/styles'
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'

export default function CustomizedMenus(props) {
    const [ anchorEl, setAnchorEl ] = React.useState(null)
    const open = Boolean(anchorEl)

    const handleClick = React.useCallback(event => {
        setAnchorEl(event.currentTarget)
    }, [])
    const handleClose = React.useCallback((hRef, fileName) => {
        if (!isEmpty(hRef) && !isEmpty(fileName) && fileName !== 'backdropClick') {
            const link = document.createElement('a')
            link.download = fileName
            link.href = hRef
            link.click()
        }
        setAnchorEl(null)
    }, [])

    return (
        <div>
            <Button
                id='customized-button'
                aria-controls={open ? 'customized-menu' : undefined}
                aria-haspopup='true'
                aria-expanded={open ? 'true' : undefined}
                variant='contained'
                disableElevation
                onClick={handleClick}
                endIcon={<KeyboardArrowDownIcon />}
            >
                Download Template Header
            </Button>
            <Menu
                id='customized-menu'
                MenuListProps={{
                    'aria-labelledby': 'customized-button',
                }}
                anchorEl={anchorEl}
                open={open}
                onClose={handleClose}
            >
                <MenuItem onClick={React.useCallback(() => handleClose('/data/User.xlsx', 'User'))} disableRipple>User</MenuItem>
                <MenuItem onClick={React.useCallback(() => handleClose('/data/Employee.xlsx', 'Employee'))} disableRipple>Employee</MenuItem>
            </Menu>
        </div>
    )
}

问题分析与修复方案

1. 缺失文件扩展名导致下载异常

当前download属性只传了文件名(如User),没有.xlsx后缀,下载后的文件无法被系统识别为Excel文件,看起来像下载失败。需要给文件名加上扩展名。

2. Menu关闭事件参数冲突

Material-UI的Menu组件onClose会自动传递事件对象或backdropClick字符串作为参数,和我们自定义的handleClose接收的hRef、fileName参数冲突,可能导致逻辑判断异常。建议把下载逻辑和菜单关闭逻辑拆分,避免参数混淆。

3. 优化a标签操作流程

创建a标签后,先添加到DOM再触发点击,完成后移除元素,提升兼容性,避免潜在的浏览器限制问题。

修改后的代码

import * as React from 'react'
import { Button, Menu, MenuItem } from '@mui/material'
import isEmpty from 'lodash/fp/isEmpty'
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'

export default function CustomizedMenus(props) {
    const [ anchorEl, setAnchorEl ] = React.useState(null)
    const open = Boolean(anchorEl)

    const handleClick = (event) => {
        setAnchorEl(event.currentTarget)
    }
    
    // 单独处理文件下载
    const handleDownload = (filePath, fileName) => {
        if (!isEmpty(filePath) && !isEmpty(fileName)) {
            const link = document.createElement('a')
            link.download = fileName
            link.href = filePath
            document.body.appendChild(link)
            link.click()
            document.body.removeChild(link)
        }
        setAnchorEl(null)
    }
    
    // 单独处理菜单关闭
    const handleClose = () => {
        setAnchorEl(null)
    }

    return (
        <div>
            <Button
                id='customized-button'
                aria-controls={open ? 'customized-menu' : undefined}
                aria-haspopup='true'
                aria-expanded={open ? 'true' : undefined}
                variant='contained'
                disableElevation
                onClick={handleClick}
                endIcon={<KeyboardArrowDownIcon />}
            >
                Download Template Header
            </Button>
            <Menu
                id='customized-menu'
                MenuListProps={{
                    'aria-labelledby': 'customized-button',
                }}
                anchorEl={anchorEl}
                open={open}
                onClose={handleClose}
            >
                <MenuItem onClick={() => handleDownload('/data/User.xlsx', 'User.xlsx')} disableRipple>User</MenuItem>
                <MenuItem onClick={() => handleDownload('/data/Employee.xlsx', 'Employee.xlsx')} disableRipple>Employee</MenuItem>
            </Menu>
        </div>
    )
}

额外检查项

  • 确认public/data/目录下确实存在User.xlsx和Employee.xlsx文件,文件名(包括大小写)完全匹配。
  • 部署后检查服务器是否正确托管public目录下的静态资源,避免被前端路由拦截。

内容的提问来源于stack exchange,提问作者KARTHIKEYAN.A

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 23:25:07