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

如何基于Express+Node.js实现单文件选择下载功能?

实现单个文件下载功能方案

前端修改

  1. 给每个文件添加独立下载按钮,传递当前文件名到下载函数
  2. 实现单文件下载逻辑,处理文件流的浏览器下载触发

修改后的前端代码:

import axios from "axios";
import { useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import './DisplayPage.css';
import NavBar from "../NavBar/NavBar";

export default function SingleDoc(){
    const{id} = useParams();
    const[place,setPlace] = useState(null);
    useEffect(()=>{
        if(!id){
            return;
        }
        axios.get(`/staff/${id}`).then(response =>{
            setPlace(response.data);
        })
    },[id]);
    if(!place) return '';

    // 单个文件下载函数
    async function downloadSingleFile(filename) {
        try {
            // 请求后端获取文件流,指定响应类型为blob
            const response = await axios.get(`/single-download`, {
                params: { id, filename },
                responseType: 'blob'
            });
            // 创建临时下载链接触发浏览器下载
            const url = window.URL.createObjectURL(new Blob([response.data]));
            const link = document.createElement('a');
            link.href = url;
            link.setAttribute('download', filename);
            document.body.appendChild(link);
            link.click();
            // 清理临时资源
            link.remove();
            window.URL.revokeObjectURL(url);
        } catch (error) {
            console.error('下载失败:', error);
        }
    };

    // 原有批量下载函数(优化下载逻辑)
    async function downloadButton(ev){
        ev.preventDefault();
        try {
            const response = await axios.get(`/download/${id}`, { responseType: 'blob' });
            const url = window.URL.createObjectURL(new Blob([response.data]));
            const link = document.createElement('a');
            link.href = url;
            link.setAttribute('download', `${id}_files.zip`);
            document.body.appendChild(link);
            link.click();
            link.remove();
            window.URL.revokeObjectURL(url);
        } catch (error) {
            console.error('批量下载失败:', error);
        }
    };

    return(
        <div>
            <NavBar/>
            <div className="single-doc">
                {place.profile.length > 0 && place.profile.map(photo => (
                        <div key={photo}>
                            <div>
                                <img className="file-pics" src={`http://localhost:4000/uploads/${photo}`} alt=''  />
                                {photo}
                                {/* 新增单个文件下载按钮 */}
                                <button onClick={() => downloadSingleFile(photo)} style={{marginLeft: '10px'}}>下载此文件</button>
                            </div>
                        </div>
                ))}
                    <div>
                        <button onClick={downloadButton}> 下载全部文件</button>
                    </div>
            </div>
        </div>
    )
}

后端修改

完善/single-download接口,通过staff ID和文件名获取对应文件,同时添加权限验证避免非法访问:

app.get("/single-download", async (req, res) => {
    try {
        const { id, filename } = req.query;
        // 获取对应staff文档
        const staffDoc = await Staff.findById(id);
        if (!staffDoc) {
            return res.status(404).send("Staff不存在");
        }
        // 验证文件名是否属于该staff的文件列表,防止路径遍历攻击
        if (!staffDoc.profile.includes(filename)) {
            return res.status(403).send("无权限下载该文件");
        }
        // 构建文件路径
        const filePath = `${__dirname}/uploads/${filename}`;
        // 返回文件供浏览器下载
        res.download(filePath, filename, (err) => {
            if (err) {
                res.status(500).send("文件下载失败");
            }
        });
    } catch (error) {
        console.error(error);
        res.status(500).send("服务器错误");
    }
});

关键说明

  • 前端通过responseType: 'blob'接收二进制文件流,再通过临时链接触发浏览器下载
  • 后端增加文件权限校验,确保用户只能下载对应staff名下的文件,避免安全风险
  • 保留原有批量下载功能,同时新增单文件下载选项

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 02:03:39